纵横研究院React技术专题社区

函数式编程在 Redux 源码中的应用

2019-11-05  本文已影响0人  Axtlive

前言

Redux 正是函数式编程的一个经典实现,本文将依据原版的 redux ,简单讲述一下 redux 的函数式编程并完成自己的一个 redux ,让大家知其然知其所以然

在这之前,你需要了解的东西:

  1. 纯函数,固定的输入就有固定的输出
  2. react 的函数组件体现就是纯函数,接受 props,输出的 view 就是纯的
  3. redux 本身就是一个容器的概念(container)
  4. react 内部有 state,就是容器(container)的值
  5. action 接受变形关系
  6. reducer 可以遍历(map)当前容器的值并执行相应修改更新操作
  7. middleware(中间件)也就是IO函子,它解决了异步的操作

redux 的目录结构

  1. createStore.js 创建一个 redux 的 store ,用来存放 state
  2. bindActionCreators.js 把 action Creators 转化成拥有同名 keys 的对象,在组件内使用的时候直接的调用
  3. applyMiddleware.js 使用自定义的中间件来扩展 redux
  4. combineReducers.js 对reducer 进行拆分,拆分后每一块独立负责管理 state 的部分
  5. compose.js 从右向左组合函数,避免柯里化函数而写出洋葱式的代码 h(g(f(x)))
  6. index.js 向外输出的

开始我们的 redux

    import createStore from "./createStore.js";
    export { createStore, combineReducers };

第一版 (createStore)

export default function createStore(initState) {
  let state = initState;
  let listeners = [];
  function subscribe(listener) {
    listeners.push(listener);
  }

 function changeState(newState) {
  state = newState;
   for (let item of listeners) {
    item();
  }
 }

  function getState() {
    return state;
  }

  return {
    subscribe,getState,changeState
  };
}

第一版总结

这个版本对状态的管理没有约束,changeState是我们改变状态的唯一方法,此时我们需要使用到 reducer,从而有了我们的第二版

第二版 (dispatch)

export default function createStore(reducer, initState) {
  let state = initState;
  let listeners = [];
  function subscribe(listener) {
    listeners.push(listener);
  }

  function dispatch(action) {
    state = reducer(state, action);     
    for (let item of listeners) {
      item();
    }
  }

  function getState() {
    return state;
  }

  return {
    subscribe,
    getState,
    dispatch
  };
}

如果在使用 createStore 过程中只传入了 reducer 并没有传入 initState ,如此就会得到一个undefined,那么我们需要进行一步操作:在 createStore.js 文件里,自身调用一下 dispatch,传入的参数为 {type:Symbol()},有用户就要说了,那我传入一个 {type:'xxxxx'}不也行吗,那假如有个开发者在 reducer 里面写了一个 type 正好就是 'xxxxx' 不就熄火了吗,所以使用的 type 的值为 Symbol 是最可靠的,可以保证不会出现重复!

第二版总结

这个版本我们加入了 reducer (业务逻辑)来修改状态使用的值,但是有个确定,非计划外的任务不能修改,也就是当你传递的 action 不在 reducer 的类型中的时候,是不会进行更改的

第三版 (combineReducer)

第三版总结

这个版本我们创建了 combineReducer 来合并多个小的 reducer 成为一个大的 reducer,使得多种逻辑处理相分离

第四版 (middleware && applyMiddleware)

    let action = doSomthing('123');
    console.log('dispatching',action)
    store.dispatch(action);
    console.log('nextState',store.getState())
    function getDispatchLog(store,action){
        let action = doSomthing('123');
        console.log('dispatching',action)
        store.dispatch(action);
        console.log('nextState',store.getState())
    }
    function logger(){
        let next = store.dispatch
        return function dispatchLog(action){
            let action = doSomthing('123');
            console.log('dispatching',action)
            next(action);
            console.log('nextState',store.getState())
        }
    }
    function myApplyMiddleware(store,middlewares){
        middlewares = middlewares.splice()
        middlewares.reverse()
        middlewares.forEach(middleware => {
            store.dispatch = middleware(store)
        })
    }
function logger(store) {
  return function wrapDispatchLog(next) {
    return function dispatchLog(action) {
      console.log('dispatching', action)
      let result = next(action)
      console.log('next state', store.getState())
      return result
    }
  }
}
    const logger = store => next => action => {
        console.log('dispatching', action)
        let result = next(action)
        console.log('next state', store.getState())
        return result
    }
    export const compose = (...funcs) {
      if (funcs.length === 0) {
        return arg => arg
      }
      if (funcs.length === 1) {
        return funcs[0]
      }
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
    export const applyMiddleWare = (...middlewares) => (createStore) => (reducer,initState) => {
        const store = createStore(reducer,initState)
        let dispatch = store.dispatch
        const middleWareAPIs = middlewares.map(item => item({
            getState: store.getState,
            dispatch: action => dispatch(action)
        }))
        dispatch = compose(...middleWareAPIs)(dispatch)
        return {
            ...store,
            dispatch
        }
    }

第四版总结

这个版本我们实现了 applyMiddleWare 来增强 dispatch,其实在使用了 middleware 以后,创建 store 的任务就交给了 中间件,它最终也是会返回一个新的 store,subscribe 和 getState 方法并没有改变,只是拥有了一个新的 dispatch 方法

总结

  1. 本文就 createStore、combinReducers、compose、applyMiddleWare 的思想对创建步骤进行了讲述
  2. 纯函数,有固定的输入,就有固定的输出
  3. createStore 作为核心部分,内部拥有subscribe、dispatch、getState 三个方法,用于订阅、通知、获取
  4. combinReducers 会对多个小的 reducer 进行合并,然后返回一个大的 reducer 来进行业务逻辑的处理
  5. compose 就是将上一个函数的执行结果作为下一个函数的参数传入
  6. applyMiddleWare 则是使用一些中间件(在 dispatch action 之后,到达 reducer 之前执行的一些操作的方式)来增强 dispatch(比如执行一下异步的操作等)
上一篇 下一篇

猜你喜欢

热点阅读