redux 封装
redux 到底要解决什么问题?管理被多个组件所依赖或者影响的状态; 一个action需要被多个reducer处理吗?
构建
$ yarn build
关于redux
actions可以被各个页面组件和reducers复用
-
各个页面(组件)如果挂载到路由,export出
mapStateToProps
,系统就会将LayoutComponent
组件或默认导出组件 与redux关联,即可使用this.props.actions
中的方法,获取到redux中的数据; -
各个页面(组件)如果不是挂载到路由上的,需要显示调用
connectComponent
或者connect
装饰器与redux的连接; -
各个页面(组件)如果已经与redux进行连接,通过
const {actions} = this.props
获取actions对象,然后调用actions.xxx()
触发action; -
mapStateToProps
用于指定redux的state中哪部分数据用于当前组件,由于reducer的combineReducers
方法包装之后,将各个reducer的state存放在对应的key中,key指的是combineReducers包装时指定的key,比如:home // 这个home就是key,es6写法utils;// src/layout/home.js{return...statehome // 这个home指的就是 combineReducers中的key...stateapp // 如果使用 ... home和app中如果有同名属性,app会覆盖home,可以通过调整...state.app,和...state.home得顺序,决定当前页面使用谁的属性。;} -
action负责准备数据,数据来源:
- 调用action方法时传入的参数
- ajax请求的异步数据
- storage/cookie中获取数据
-
reducer为纯函数,负责处理数据,要对state做deepcopy,返回一个新的数据,不要直接操作state,不会涉及异步,不操作Storage,单纯的获取action的数据之后,做进一步处理。
-
store负责将数据以props形式传递给component,以及通过中间件对数据统一处理。
-
组件,调用触发action,获取store处理过的数据,不发送ajax,不操作storage,单纯的展示数据。
-
适当的区分哪些数据需要redux处理,哪些数据直接使用state,不要为了redux而redux
- 哪些数据适合使用redux处理?
- 异步数据(包括ajax,websocket,异步状态loading等)
- 来自cookie/localStorage等其他存储的数据
- 多组件公用数据
- 多组件间通信数据
- 哪些数据直接使用组件内部state即可?
- 不涉及组件外数据修改(比如ajax修改后端数据),不被其他任何外部组件使用的数据,比如:点击显示隐藏modal;点击展开收起div等控制内部状态的数据。
- 哪些数据适合使用redux处理?
action:
- action 使用的是
redux-actions
模块构建的Flux Standard Action
- 各个action文件之间,不允许出现同名方法,
src/actions/index.js
中有检测。
回调处理
调用actions方法时,给actions方法传入一个回调参数,这个回调参数,最终是由 createAction
的 metaCreator
参数处理的,项目中做了封装。metaCreator
可以携带业务以外的数据,异步actions会触发两次reducer,第一次触发时payloadCreator
传递给reducer的是promise对象,无法携带其他数据了,这时候就可以通过metaCreator
携带额外的数据。
const testAsync = ; /* 解释 (onResolve, onReject)=> { return { onResolve, onReject, sync: 'home' }}就是 createAction 的第三个参数 metaCreator,是一个函数,这个函数返回的数据,最终存放在action的meta中。返回数据 onResolve 和 onReject 就是调用action方法时传入的回调函数,一个代表成功,一个代表失败,这两个数据最终会被 src/store/asyncActionCallbackMiddleware.js中间件使用*/
异步写法
异步是使用src/store/promise-middleware.js
中间件进行处理的
一本异步action其实是触发了两次reducer,第一次标记异步开始,reducer可以获取相应的标记,第二次异步完成,返回数据。具体可以参考promise-middleware.js
源码
action异步写法
;;; const saveUserMessage = ;
reducer 异步写法:
有两种写法,第一种有机会获取所有action的数据,第二种,只能获取自己type的action数据,个人觉得获取所有action数据没有用,反而状态受干扰。推荐第二种写法
; let initialState = isSidebarCollapsed: false fetching: false; { const payload error meta={} type} = action; const sequence = {}} = meta; const status = sequencetype === 'start'; if status || error // 出错,或者正在请求中,注意: 这种写法将捕获所有异步action,自己模块得status要在自己的case中写。 return ...state fetching: status ; }
;; let initialState = loading: false orderState: ''; { const error meta = {}} = action; const sequence = {} = meta; const loading = sequencetype === 'start'; // loading 要反应到页面上, // error由middleware处理,全局message提示,或者各个页面添加回调处理 if loading || error return ...state loading ; return ...state orderState: 'success' loading ; }} initialState;
redux中的异常处理
- 基于
flux-standard-action
规范,异常action返回结构为:{..., payload: error, error: true, ...}
utils-middleware.js
会统一截获处理异常(无论异步还是同步), 会根据meta.errorTip
来确定是否全局提示处理异常信息async-action-callback-middleware.js
会调用actions的回调函数,给具体页面处理异常的机会
异步异常
异步操作统一使用的是promise,异常捕获在src/store/promise-middleware.js
中间件中,一旦异步操作出现异常,action将传递给相应的reducer{..., payload: error, error: true, ...}
同步异常
如果action
返回的payload
是一个Error
对象,redux-actions
,将自动设置action.error
为true
自己可以在action中,使用try-catch
处理???
将数据存储到localStorage中
开发过程中只需要在action中添加sync标记即可实现state存储到localStorage。 以及src/actions/utils.js 的 getStateFromStorage 方法中维护对应的sync,即可同步localStorage到state中。
action中通过 metaCreator 的 sync 属性来标记这个action相关的state是否存储到localStorage中。其中sync将会作为存储数据的key
const setSettings = ;
使用 sync-reducer-to-local-storage-middleware.js 中间件进行存储操作:
;;; { if ! return ; const meta = {} sequence = {} error payload} = action; const sync = meta; if actiontype === typesSYNC_STATE_TO_STORAGE let state = ; try storage; catch err /* eslint-disable */ console; if !sync || sequencetype === 'start' || error return ; ; ;};
项目启动的时候,会在src/layouts/app-frame/AppFrame.jsx 中调用 actions.getStateFromStorage(); 方法,将localStorage中的数据同步到state中
actions.getStateFromStorage(); 在 src/actions/utils.js中实现:
// 同步本地数据到state中const getStateFromStorage = ;
撤销&重做
通过 redux-undo 可以实现撤销&重做功能
通过undoable 对reducer进行包装,就可以实现撤销&重做功能
import undoable, {includeAction} from 'redux-undo';
...
export default undoable(organization, {
filter: includeAction([types.SET_ORGANIZATION_TREE_DATA]),
limit: 10,
undoType: types.UNDO_ORGANIZATION,
redoType: types.REDO_ORGANIZATION,
});