理解 Redux 的最好方式,是本身写一个

react-redux 是 React 生态中比较(若是不是最的话)流行的一种状态管理方式,而它所依托的 redux,继承了 flux 的衣钵,而且引入了单一数据源、state 为只读、只能经过纯函数修改三大原则。一个最简单的 redux 应用示例:javascript

// https://cn.redux.js.org/
import { createStore } from 'redux'

/** * 这是一个 reducer,形式为 (state, action) => state 的纯函数。 * 描述了 action 如何把 state 转变成下一个 state。 * * state 的形式取决于你,能够是基本类型、数组、对象、 * 甚至是 Immutable.js 生成的数据结构。唯一的要点是 * 当 state 变化时须要返回全新的对象,而不是修改传入的参数。 * * 下面例子使用 `switch` 语句和字符串来作判断,但你能够写帮助类(helper) * 根据不一样的约定(如方法映射)来判断,只要适用你的项目便可。 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// 建立 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter)

// 能够手动订阅更新,也能够事件绑定到视图层。
store.subscribe(() => console.log(store.getState()))

// 改变内部 state 唯一方法是 dispatch 一个 action。
// action 能够被序列化,用日记记录和储存下来,后期还能够以回放的方式执行
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
复制代码

createStore 是 redux 中最基础的 API,它的做用就是返回一个 store;而这个 store 有三个方法 —— dispatchsubscribegetState。其实从方法名上,咱们大体能够推测出 redux 应用了订阅发布模式;该模式能够直接套用下面的模板代码:java

// 开始
// createStore 是一个闭包,维护了 currentState, listeners 私有性;
// 外层想要获取 state,只能经过 getState;
// listeners 没法从外层修改
function createStore(reducer, preloadedState) {
  // 每次调用 subscribe,就会向 listeners 数组 push 一个 listener;
  // 每次调用 dispatch,listeners 数组中的函数会依次执行;
  const listeners = [];
  let currentReducer = reducer;
  let currentState = preloadedState;

  function dispatch(action) {
    // 正如示例代码中所展示的,reducer(即 counter) 函数接受 state,action 两个参数
    // action 是一个简单对象,有一个 type 属性;
    // reducer 中根据 type 执行对应操做,返回新的 state;
    currentState = currentReducer(currentState, action);
    // 执行 listeners 数组中的全部函数
    listeners.forEach(listener => listener());
  }

  function subscribe(listener) {
    listeners.push(listener);
    // 返回一个取消订阅的函数
    return function unsubscribe() {
      let index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    };
  }

  // 这个函数的做用就是返回 state
  function getState() {
    return currentState;
  }

  return {
    dispatch,
    subscribe,
    getState
  };
}

function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

// 测试一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
复制代码

上面的代码只是为了大体解释一下 redux 的运行机制,实际上是有很多问题的,好比 action 只能是一个简单对象,但咱们的代码中并无进行检测;咱们暂且不去关注这些问题,而是先去实现功能。接下来,修改 dispatch,使它支持函数参数:react

function dispatch(action) {
  // 若是传入一个函数,则为这个函数注入 dispatch 和 getState 方法,
  // 而且执行这个 action
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }
  // 正如示例代码中所展示的,reducer(即 counter) 函数接受 state,action 两个参数
  // action 是一个简单对象,有一个 type 属性;
  // reducer 中根据 type 执行对应操做,返回新的 state;
  currentState = currentReducer(currentState, action);
  // 执行 listeners 数组中的全部函数
  listeners.forEach(listener => listener());
  return action;
}
复制代码

用异步的方式,测试一下:redux

function counter(state = 0, action) {
 switch (action.type) {
   case 'INCREMENT':
     return state + 1;
   case 'DECREMENT':
     return state - 1;
   default:
     return state;
 }
}

function asyncAdd(dispatch) {
 return new Promise((resolve, reject) => {
   setTimeout(() => {
     resolve(
       dispatch({
         type: 'INCREMENT'
       })
     );
   }, 1000);
 });
}
// 测试一下
let store = createStore(counter);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch(asyncAdd).then(() => store.dispatch(asyncAdd)); // 1s 后 3, 2s 后 4
复制代码

如今,咱们的代码已经具有了 redux + redux-thunk 的功能,并且没有增长新的 API。但咱们是用入侵的方式修改了 dispatch,若是要增长其余新功能怎么办?继续修改 dispatch 么?显然不能这么干。redux 使用中间件的机制,来修改 dispatch:数组

function compose(...funcs) {
  if (funcs.length === 0) {
    return args => args;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = () => {
      throw new Error('not valid');
    };

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    };

    const chain = middlewares.map(middleware => middleware(middlewareAPI));
    // applyMiddleware 本质上,仍是须要去覆盖原来的 dispatch 方法
    dispatch = compose(...chain)(store.dispatch);

    return {
      ...store,
      dispatch
    };
  };
}

// 咱们的 thunk
const thunk = ({ dispatch, getState }) => next => action => {

  // 这段代码,和咱们上面暴力修改 dispath 的方法,实际上是同样的
  if (typeof action === 'function') {
    return action(dispatch, getState);
  }

  return next(action);
};
复制代码

以上就是 redux 以及 redux-thunk 的简单实现,有很多 bug,但愿对你们理解 redux 有点帮助;也建议你们去看看 redux 的源码,其实源码也很短,而且注释很完整。数据结构

相关文章
相关标签/搜索