一, redux 基础
redux
通过 dispatch(action) -> 中间件 -> reducer 处理数据 -> 改变 store -> 使用 subscribe() 监听 store 改变更新视图 的方式管理状态
将所有状态存储在一个 store 对象里面
reducer 为纯函数, 而异步操作由于结果的不确定性所以含有副作用, 所以需要特殊处理
react-redux
容器组件, 负责管理数据和业务逻辑, 不负责 UI 呈现
UI 组件, 提供 UI 呈现, 无状态即不使用 this.state, 状态全部由 this.props 提供
由 connect 生成容器组件, 每次 store 改变会调用 connect,connect 接收两个参数: mapStateToProps, mapDispatchToProps
mapStateToProps, 将状态映射到 UI 组件的 props
mapDispatchToProps, 将 dispatch 方法映射到 UI 组件的 props
Provider 组件, 使用 content API 将 store 从顶层开始传到每一层 component 供 connect 使用
二, redux 处理异步的中间件
redux-thunk
redux-thunk 中间件允许 action 是一个方法
中间件收到 action 后会执行 action 方法并将结果提供给 reducer
action 混乱导致不易维护
redux-saga
saga 会监听 action 并基于这个 action 执行 Effects 操作
Effects 提供灵活的 API, 包括阻塞, 非阻塞调用, 取消, 等待, race 等操作
方便隔离并执行异步操作, 并易于测试
三, redux-request-async-middleware
先从 redux 文档中的异步 action 说起, 每个接口调用需要 dispatch 三个同步 action, 分别是:
一种通知 reducer 请求开始的 action. 对于这种 action,reducer 可能会切换一下 state 中的 isFetching 标记. 以此来告诉 UI 来显示加载界面.
一种通知 reducer 请求成功的 action. 对于这种 action,reducer 可能会把接收到的新数据合并到 state 中, 并重置 isFetching.UI 则会隐藏加载界面, 并显示接收到的数据.
一种通知 reducer 请求失败的 action. 对于这种 action,reducer 可能会重置 isFetching. 另外, 有些 reducer 会保存这些失败信息, 并在 UI 里显示出来.
也就是一个接口发起是这样的
- dispatch(fetchPostsRequest(subject));
- fetch(url).then(res => {
- dispatch(fetchPostsSuccess(subject, res));
- }).catch(e => {
- dispatch(fetchPostsFailure(subject, e));
- })
只是将这个操作封装进中间件里, 特殊的地方在于:
所有的异步请求共用这三个 action
用 subject 来区分是哪一个请求
将所有的结果都放到 store.requests 里
中间件源码
- export const reduxRequest = store => next => action => {
- let result = next(action);
- let { type, subject, model } = action;
- let _next = action.next;
- if(type === FETCH_POSTS_REQUEST) {
- model().then(response => {
- _next && _next(response);
- store.dispatch(fetchPostsSuccess(subject, response));
- }).catch(error => {
- console.error(error);
- store.dispatch(fetchPostsFailure(subject, error));
- });
- }
- return result
- };// 欢迎加入全栈开发交流圈一起学习交流: 864305860
和 redux-thunk 一样, 将方法放进 action 里
中间件拦截 FETCH_POSTS_REQUEST action, 并进行异步处理
reducer 源码
- export const requests = (state = {}, action) => {
- switch (action.type) {
- case FETCH_POSTS_REQUEST:
- return assign({},
- state,
- {
- [action.subject]: {
- isFetching: true,
- state: 'loading',
- subject: action.subject,
- response: null,
- error: null,
- }
- }
- );
- case FETCH_POSTS_FAILURE:
- return assign({},
- state,
- {
- [action.subject]: {
- isFetching: false,
- state: 'error',
- subject: action.subject,
- response: state[action.subject].response,
- error: action.error,
- }// 欢迎加入全栈开发交流圈一起学习交流: 864305860
- }// 面向 1-3 年前端人员
- );// 帮助突破技术瓶颈, 提升思维能力
- case FETCH_POSTS_SUCCESS:
- return assign({},
- state,
- {
- [action.subject]: {
- isFetching: false,
- state: 'success',
- subject: action.subject,
- response: action.response,
- }
- }
- );
- case FETCH_POSTS_CLEAR:
- return assign({},
- state,
- {
- [action.subject]: {
- isFetching: false,
- state: 'cleared',
- subject: null,
- response: null,
- error: null,
- }// 欢迎加入全栈开发交流圈一起学习交流: 864305860
- }// 面向 1-3 年前端人员
- );// 帮助突破技术瓶颈, 提升思维能力
- default:
- return state;
- }
- }
将结果放入该 subject 对应下的 response, 如果错误的话将错误信息放入 error 当中
isFetching 表示当前的请求状态
另外还加入了当前的状态 state 和 subject 信息
将请求进行封装
- const request = (subject, model, next) => {
- _dispatch(fetchPostsRequest(subject, model, next));
- return true;
- };
写一个方法来发起 FETCH_POSTS_REQUEST action
也就是说写请求的时候不用再管 action 这东西了, 直接调用 request 方法
将结果进行封装
- const getResponse = state =>
- state
- && state.response !== null
- && state.response;
- const getLoading = (states = []) =>
- states.reduce((pre, cur) =>
- pre || (cur && cur.isFetching)
- , false)// 欢迎加入全栈开发交流圈一起学习交流: 864305860
- || false;// 面向 1-3 年前端人员
- // 帮助突破技术瓶颈, 提升思维能力
可以获取结果和多个请求下 loading 的状态
有更多的操作或者格式还可以继续封装, 比如列表
四, 总结
使用了 redux 来进行状态管理, 而并不需要编写 redux 那一套复杂逻辑, 最大程度的减少异步操作的复杂度
适用于前端通过接口来处理和存储数据的项目
接口由 redux 处理, 而视图组件由内部 state 来处理, 而外部只暴露简单的接口来进行操作, 分离业务层和视图层
对比 react 16.3 new content API,redux 的优势在于热插播的中间件和纯函数 reducer 写法
来源: http://www.qdfuns.com/article/51117/aed8e87889eda24b557db6279cb11d52.html