用redux管理数据状态

用redux管理数据状态

动机

应用模块之间须要访问共享数据,采用redux管理数据状态。全部数据保存在store tree中,用于维护数据状态。
“Redux 是 JavaScript 状态容器,提供可预测化的状态管理。”html

目录

  • 三大原则react

  • combineReducersgit

  • createStoregithub

  • store方法redux

  • 数据流api

  • middlewares数组

  • 实现redo&undo闭包

三大原则

单一数据源

整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。app

State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。经过 store.dispatch() 将 action 传到 store。action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做异步

let action = {
    type: 'CHANGE_TEXT',
    text:'helloworld'
};
store.dispatch(action);

能够经过Action 建立函数 生成 action 。

function changeText(text) {
  return {
    type: 'CHANGE_TEXT',
    text
  }
}
store.dispatch(changeText('helloworld'));

使用纯函数来执行修改

经过reducer改变state tree,要求reducer是纯函数,即只要传入参数相同,返回计算获得的下一个 state 就必定相同。没有特殊状况、没有反作用,没有 API 请求、没有变量修改,单纯执行计算。

function todoApp(state = initialState, action) {
  switch (action.type) {
    case 'CHANGE_TEXT':
      return Object.assign({}, state, {
        text: action.text
      })
    default:
      return state
  }
}

注意:
1)不要修改 state。
2)在 default 状况下返回旧的 state。

combineReducers

开发一个函数来作为主 reducer,它调用多个子 reducer 分别处理 state 中的一部分数据,而后再把这些数据合成一个大的单一对象。每一个 reducer 只负责管理全局 state 中它负责的一部分。每一个 reducer 的 state 参数都不一样,分别对应它管理的那部分 state 数据。

reducers.js
import { combineReducers } from 'redux'
const initialUser = {
 name:"chen",
 age:10
};
const initialJob = {
 position:"engineer"
};
const userReducer = (state = initialUser, action) => {
 let payload = action.payload;
 let type = action.type;
 switch (type) {
   case "CHANGE_NAME":
     state = Object.assign({},state,{ name : payload});
     break;
   case "ADD_AGE":
     state = Object.assign({},state,{ age : state.age+1});
     break;
   default:
     break;
 }
 return state;
};
const jobReducer = (state = initialJob, action) => {
 let payload = action.payload;
 let type = action.type;
 switch (type) {
   case "CHANGE_POSITION":
     state = Object.assign({},state,{ position : payload});
     break;
   default:
     break;
 }
 return state;
};

const reducers = combineReducers({
 userReducer,
 jobReducer
})

export default reducers

createStore

经过createStore生成store tree。createStore() 的第二个参数是可选的, 用于设置 state 初始状态

const store = createStore(
  reducers
);

store方法

提供 getState() 方法获取 state;
提供 dispatch(action) 方法更新 state;
经过 subscribe(listener) 注册监听器;
经过 subscribe(listener) 返回的函数注销监听器。

数据流

严格的单向数据流
数据流动方向

clipboard.png

问题:没法进行异步或辅助操做。

middlewares

提供了位于 action 被发起以后,到达 reducer 以前的扩展点。 能够用来进行日志记录、建立崩溃报告、调用异步接口或者路由。

如下是3个不一样功能的中间件,经过输出数组的方式将3个中间件输出模块

middleware 机制

redux 提供了 applyMiddleware 这个 api 来加载 middleware

applyMiddleware.js源码
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
middlewares
const m1 = store => next => action => {
  let startState = store.getState();
  console.log("m1 start");
  console.log(startState.user.age);
  next(action);
  let endState = store.getState();
  console.log("m1 end");
  console.log(endState.user.age);
};
const m2 = store => next => action => {
  let startState = store.getState();
  console.log("m2 start");
  console.log(startState.user.age);
  next(action);
  let endState = store.getState();
  console.log("m2 end");
  console.log(endState.user.age);
};
const m3 = store => next => action => {
  let startState = store.getState();
  console.log("m3 start");
  console.log(startState.user.age);
  next(action);
  let endState = store.getState();
  console.log("m3 end");
  console.log(endState.user.age);
};

const middlewares = [m1, m2, m3];

export default middlewares
index.js
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
import middlewares from './middlewares';

import reducers from './reducers';
import middlewares from './middlewares';

const defaultState = {};
const store = applyMiddleware(...middlewares)(createStore)(reducers, defaultState);
window.store = store;

applyMiddleware 是一个多层柯里化(curry)的函数
经过applyMiddleware(...middlewares)能够将[m1,m2,m3]3个中间件串联起来,下面分析如下具体实现方式。
1)初始化store将dispatch指向store.dispatch

const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch

2)用middlewareAPI封装store方法,middlewareAPI.dispatch方法最终指向store.dispatch。

let chain = [] 
const middlewareAPI = {
   getState: store.getState,
   dispatch: (action) => dispatch(action)
}

clipboard.png

3)经过将middlewareAPI传入每一个中间件[m1,m2,m3],返回chain,此时chain=[f1,f2,f3],且因为闭包,f1,f2,f3中的store都指向middlewareAPI ,最终指向store。这样的好处就是每一个f都能访问到同一个store。

chain = middlewares.map(middleware => middleware(middlewareAPI))

clipboard.png
4)经过compose函数将[f1,f2,f3] 串联执行,最后dispatch被改写成了以下函数。

dispatch = f1(f2(f3(store.dispatch)));
/*
dispatch = (action)=>{
  console.log("m1 start");
  ((action) =>{
    console.log("m2 start");
    ((action) => {
      console.log("m3 start");
      store.dispatch(action)
      console.log("m3 end");
    })(action);
    console.log("m2 end");
  })(action);
  console.log("m1 end");
}
*/

clipboard.png

能够看到,此时f1’,f2’,f3’ 中的next指向中,只有最后一个f3’是指向store.dispatch,其他next指向前一个f'的输出。f1’,f2’,f3’全部store都指向middlewareAPI,最终getState和dispatch仍是指向store。经过这样的方式能够将中间件串联起来。

例子

执行一次dispatch

store.dispatch({type:"ADD_AGE"})

输出结果以下:
clipboard.png

注意

中间件的串联并非简单依次执行,而是从middlewares数组的右边开始,依次将后一个中间件输出当成的函数(一个接收action的函数)做为前一个的next。

总结

最后,通过采用middleware,咱们加入middleware来实现“非纯操做”,如请求异步接口,进队列,出队列,处理数据等。

加入middleware后的数据流

clipboard.png

参考文献

https://zhuanlan.zhihu.com/p/...
https://github.com/reactjs/redux

相关文章
相关标签/搜索