可能是短期内关于react的对后一篇笔记。假设读者对redux和router4有基本了解。
缘由:
现在网上很多关于react+redux的文章都是沿用传统的文件组织形式,即:
|--components |--reducers |--actionTypes |--actions |--else
这样的目录形式。这种形式的一个好处是:store中的状态一开始就是完整的,在按需加载的时候不需要特意更新root reducer和全局state树,只加载对应的视图即可。
但是,笔者在学习《深入浅出react+redux》的时候,作者提出的文件组织形式是以组件为单元组织app的文件结构,即:
|--ComponentA |--action |--reducer |--View |--index.js |--ComponentB |--action |--reducer |--...
此时app的store和root reducer在一开始只有公共部分,按需加载某个组件后,再对redux store和root reducer进行更新扩充。
这种情况下,最关键的问题是:如何在每次按需加载后更新reducer和store。实践上分为两个阶段:
1.异步加载组件。组件的reducer、view、state字段作为对外暴露的接口。
2.加载完成后获取其reducer和state字段更新root reducer & redux store。
《深入浅出》中作者给的方法是:
1.用webpack的require.ensure分片组件,实现异步加载。
2.借助store enhancer拓展一个store.reset方法,将新组件的reducer和state字段传进去,完成更新。
然而现在要使用router4,怎么解决上面问题呢?
我目前的方案是:
1.异步加载使用Bundle组件(下面会给出其代码)。
2.刷新store和root reducer仍然使用《深入浅出》作者给出的reset方法。
----------------------------------------------------------分割-----------------------------------------------
我们现在已经会使用redux管理app状态。将state提取到全局store,并经由disptach方法派发action给root reducer 生成新的store state,而组件本身以观察者监听state变化。
router4遵循组件即路由的思想,url匹配时由Route标签渲染我们的组件。所以本质上<Route>也是视图。与Redux毫无冲突。
它们一个限制了我们app的数据流,一个控制我们视图的跳转。
由于react-redux提供的Provider组件必须放到应用的最顶层以实现全局访问store,所以项目的最基本骨架是:
|--Provider |--App |--Router |--RouteA |--RouteA child |--RouteB //...
在Router4中,我们往往用到的是Bundle组件来异步加载组件,我对它进行一些改写,以适应“按组件组织文件”的方式:
1 import React from 'react'; 2 import PropTypes from 'prop-types'; 3 import { 4 combineReducers 5 } from 'redux'; 6 import store from './Store.js'; 7 class Bundle extends React.Component { 8 constructor(props) { 9 super(props); 10 this.state = { 11 mod: null 12 }; 13 this.page=null; 14 } 15 componentWillMount() { 16 // 加载初始状态 17 this.load(this.props); 18 } 19 20 componentWillReceiveProps(nextProps) { 21 if (nextProps.load !== this.props.load) { 22 this.load(nextProps); 23 } 24 } 25 26 load(props) { 27 // 重置状态 28 this.setState({ 29 mod: null 30 }); 31 // 传入组件的组件 32 props.load().then((mod) => { 33 //... 34 this.page=mod.Page; 35 let {Page,stateKey,reducer,initialState} = mod; 36 let state=store.getState(); 37 38 store.reset(combineReducers({...store._reducers,[stateKey]:reducer}),{...state,[stateKey]:initialState}); 39 40 this.setState({ 41 // handle both es imports and cjs 42 mod: mod.Page.default ? mod.Page.default : mod 43 }); 44 }); 45 } 46 47 render() { 48 // if state mode not undefined,The container will render children 49 return this.state.mod ? this.props.children(this.page) : '加载中,请稍候...'; 50 } 51 } 52 53 Bundle.propTypes = { 54 load: PropTypes.func, 55 children: PropTypes.func 56 }; 57 58 export default Bundle;