A Better File Structure For React/Redux Applications
来源:互联网 发布:文本转换语音软件 编辑:程序博客网 时间:2024/06/14 23:33
https://marmelab.com/blog/2015/12/17/react-directory-structure.html
Most of the examples I could find about React/Redux applications (either client side or universal) are very simple. They choose to organize files by nature (action, component, container, reducer). The result is a directory structure looking like the following:
actions/ CommandActions.js UserActions.jscomponents/ Header.js Sidebar.js Command.js CommandList.js CommandItem.js CommandHelper.js User.js UserProfile.js UserAvatar.jscontainers/ App.js Command.js User.jsreducers/ index.js command.js user.jsroutes.js
The Redux Book follows this convention, and I know at least two Redux boilerplate repositories following it, too: 3ree, and react-redux-universal-hot-example.
That’s nice, but what happens when I need to add code about a new domain, including actions, components, and a reducer? For instance, if I want to deal with a catalog of products, I need to add files in all of these directories, ending with:
actions/ CommandActions.js ProductActions.js <= Here UserActions.jscomponents/ Header.js Sidebar.js Command.js CommandList.js CommandItem.js CommandHelper.js Product.js <= Here ProductList.js <= Here ProductItem.js <= Here ProductImage.js <= Here User.js UserProfile.js UserAvatar.jscontainers/ App.js Command.js Product.js <= Here User.jsreducers/ index.js foo.js bar.js product.js <= Hereroutes.js
You see where this is going. Fast forward two months from now, and the components/
directory contains dozens of files, and I need to open 4 files in 4 different directories each time I touch a single feature.
Why not group files by domain instead? To make the difference between actions, components, and reducers, I can still use a file suffix:
app/ Header.js Sidebar.js App.js reducers.js routes.jscommand/ Command.js CommandContainer.js CommandActions.js CommandList.js CommandItem.js CommandHelper.js commandReducer.jsproduct/ Product.js ProductContainer.js ProductActions.js ProductList.js ProductItem.js ProductImage.js productReducer.jsuser/ User.js UserContainer.js UserActions.js UserProfile.js UserAvatar.js userReducer.js
I can make things even a little easier to read by merging a container and the related component. Redux makes the difference between containers, which are connected to the state, and components, which are dumb and stateless. Most tutorials reflect this difference with two separate files:
// in Product.jsexport default function Product({ name, description }) { return <div> <h1>{ name }</h1> <div className="description"> {description} </div> </div>}// in ProductContainer.jsimport { bindActionCreators } from 'redux';import { connect } from 'react-redux';import * as ProductActions from './ProductActions';import Product from './Product';function mapStateToProps(state) { return {...state};}function mapDispatchToProps(dispatch) { return bindActionCreators({ ...ProductActions, }, dispatch);}export default connect(mapStateToProps, mapDispatchToProps)(Product);
The only practical interest to separate component and container is to facilitate the unit tests of the component (without using Redux at all). In 99% of the cases, the component is never used outside of the container. Well, ES6 allows to export
more than one element, right? Then I can merge those two scripts into a single file, where the export default
is the container, and export Product
is the component:
// in Product.jsimport { bindActionCreators } from 'redux';import { connect } from 'react-redux';import * as ProductActions from './ProductActions';// component partexport function Product({ name, description }) { return <div> <h1>{ name }</h1> <div className="description"> {description} </div> </div>}// container partfunction mapStateToProps(state) { return {...state};}function mapDispatchToProps(dispatch) { return bindActionCreators({ ...ProductActions, }, dispatch);}export default connect(mapStateToProps, mapDispatchToProps)(Product);
That way, a unit test on the component can simply import { Product } from './Product.js'
. Now the directory structure counts one less file per directory:
app/ Header.js Sidebar.js App.js reducers.js routes.jscommand/ Command.js // component & container CommandActions.js CommandList.js CommandItem.js CommandHelper.js commandReducer.jsproduct/ Product.js // component & container ProductActions.js ProductList.js ProductItem.js ProductImage.js productReducer.jsuser/ User.js // component & container UserActions.js UserProfile.js UserAvatar.js userReducer.js
And while we’re talking about tests, they usually live in their own test/
directory, far from the runtime code:
src/ app/ Header.js Sidebar.js App.js reducers.js routes.js command/ Command.js CommandActions.js CommandList.js CommandItem.js CommandHelper.js commandReducer.js product/ Product.js ProductActions.js ProductList.js ProductItem.js ProductImage.js productReducer.js user/ User.js UserActions.js UserProfile.js UserAvatar.js userReducer.jstest/ app/ Header.js Sidebar.js App.js reducers.js routes.js command/ Command.js CommandActions.js CommandList.js CommandItem.js CommandHelper.js commandReducer.js product/ Product.js ProductActions.js ProductList.js ProductItem.js ProductImage.js productReducer.js user/ User.js UserActions.js UserProfile.js UserAvatar.js userReducer.js
I find it harder to spot missing tests for components, or to navigate the file structure once the domain expands. So I try to keep tests in the same directory as the element they test - simply using a -spec.js
suffix. If this were Python, tests would even be in the same file! All the scripts related to a bounded context, including tests, are now grouped in a single directory - easy to read and reason about.
src/ app/ Header.js Header-spec.js Sidebar.js Sidebar-spec.js App.js App-spec.js reducers.js reducers-spec.js routes.js routes-spec.js command/ Command.js Commands-spec.js CommandActions.js CommandActions-spec.js CommandList.js CommandList-spec.js CommandItem.js CommandItem-spec.js CommandHelper.js CommandHelper-spec.js commandReducer.js commandReducer-spec.js product/ Product.js Product-spec.js ProductActions.js ProductActions-spec.js ProductList.js ProductList-spec.js ProductItem.js ProductItem-spec.js ProductImage.js ProductImage-spec.js productReducer.js productReducer-spec.js user/ User.js User-spec.js UserActions.js UserActions-spec.js UserProfile.js UserProfile-spec.js UserAvatar.js UserAvatar-spec.js userReducer.js userReducer-spec.js
Configuring the test runner (either Jest or Mocha) is easy: just make it run the tests in ./src/**/*-spec.js
.
This directory structure grows well with the project size. And when it’s time to split an app into independent repos to ease reusability across projects, then the code refactoring is really lightweight. I highly recommend it!
Tip: If you’re interested in advanced Redux usage, check out admin-on-rest, a React toolkit for building admin interfaces on top of REST APIs that we released in 2016.
Edit: It seems the Reddit community heard about this post; the discussion continues there: https://www.reddit.com/r/reactjs/comments/47mwdd/a_better_file_structure_for_reactredux/.
- A Better File Structure For React/Redux Applications
- Folder Structure for Single Page Applications
- react+redux+react-redux
- For A Better Android Experience
- Ten tips for building better Adobe AIR applications
- redux react-redux
- Redux(三: React-Redux)
- react-redux
- React + Redux
- React&Redux
- react-redux
- React Redux
- react-redux
- react-redux
- React-Redux
- React Redux
- react + redux + react-redux 心得
- How to Create Dump File for Applications
- NRF52832 watchdog
- 百度地图API & 高德地图API 使用问题汇总
- Linux进程间通信——使用命名管道
- 基于Streaming I/O的V4L2设备使用
- vue2导航根据路由传值,而改变导航内容
- A Better File Structure For React/Redux Applications
- arduino图形化编程入门
- 【软件工具】(二)markdown基本语法
- 时间选择器(二)
- LintCode : 删除链表中的重复元素
- MyBatis实现单表增删改查(CURD)--属性名与字段名不一致问题
- 手机登录页面(记住密码)
- (error) MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on d
- tableau汇总数据排序(正常不能排序)