redux 闲谈

栏目: 服务器 · 发布时间: 6年前

内容简介:redux 遵循严格的单向数据流,以React为例如下图:(网图,侵删)

redux 闲谈

起因: 在与涂鸦智能一个web工程师交流过程中,他询问我dispatch一个action,是如何和reducer 绑定的,dispatch(actionA)只会触发reducerA却不会去触发reducerB.

Github https://github.com/reduxjs/redux

redux 数据流程

redux 遵循严格的单向数据流,以React为例如下图:

redux 闲谈

(网图,侵删)

store.getState()

初始化

redux 的 Store 初始化通过 createStore 方法来进行初始化

const store = createStore(combineReducers, prevState, compose(applyMiddleware(...middleware)))
  1. combineReducers 合并后的reducer,reducer 形式如下
function authReducer(state, action) {

    switch(action.type) {
        case 'login':
            return { ...state, isLogin: true }
        default:
            return {...state}
    }
}

function userReducer(state, action) {
    // ...如上
}

通过使用combineReducers({ authReducer, userReducer }) 返回一个reducers

prevState
middleware

该部分初始化流程阶段,在下面applyMiddleware 会再次调用

combineReducers 合并 reducer

在上面说到我们可能存在多个reducer,也可能分模块来处理不同的状态问题,这里就需要合并不同模块的reducer, 实现代码 :

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    
    // 其他代码...

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key] // ①
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)
  
  // 其他代码...
  
  return function combination(state = {}, action) { // ②
    // 其他代码...
    
    let hasChanged = false
    const nextState = {}
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action) // ③
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    return hasChanged ? nextState : state
  }
}

传入一个reducers = { authReducer, userReducer }, 很明显对reducers 进行了对象遍历,在①这个位置进行了单个reducer函数的拷贝,在②这个位置redux 内部自己创建了一个reducer函数为 combination , 在是③这个位置,进行了开发者定义的reducer定义,也就是说dispatch(action) -> combination(state, action) -> customReducer(state, action), 在循环内部每次获取对应的module 的state值作为 previousStateForKey , 传入用户的reducer中,所以用户dispatch的 action中type是和reducer对应的位置在于用户自己的判断

compose 和 applyMiddleware

compose

compose 函数非常简短, 代码 如下:

export default function 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)))
}

传入一个不定量函数作为参数,主要作为函数从一个数组形式例如[login, fetchToken, fetchUser]这样的函数传入,得到则是fetchUser(fetchToken(login(...args))) 这样的形式, 将函数组合起来,并从右到左,而且最右侧函数可以接受多个参数

示例仅仅为了说明,不在实际业务中出现

applyMiddleware

根据函数字面意思该函数为应用redux 中间件, 核心代码 如下:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => { // 这里createStore 通过初始化时候传入
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI)) // ①
    dispatch = compose(...chain)(store.dispatch) // ②

    return {
      ...store,
      dispatch
    }
  }
}

示例logger

const logger = store => next => action => { // example-①
  console.log('dispatching', action)
  let result = next(action)
  console.log('next state', store.getState())
  return result
}

// usage: applyMiddleware(logger)

这里applyMiddleware 接受不定量的redux中间件,我们就来解释一下 example-① 这里申明是哪里来的。

applyMiddleware 源码中,传入middlewares后,在 ① 的位置就行了第一个次的中间件调用传入middlewareAPI,分别为 getStatedispatch 两个方法这里对应 example-① 中的 store,在 ② 的位置进行了上文说的 compose 调用,把所有的中间件进行了组合,从右到左的调用,此刻传入dispatch 方法,这里方法对应 example-① 中的 next ,在上文中说到 compose 对 函数进行了组合,我们这里将store.dispatch 传入当成参数,返回一个新的函数等价于我们在ViewUI 中dispatch的时候其实使用的 compose(...chain)(store.dispatch)(action) 这样的方式,所以在 example-① 中action 是开发者的action。使用中间件后的store.dispatch也是通过中间件包装后的dispatch。在最后 applyMiddleware 把dispatch 返回。

这里有点晦涩难懂在于compose(...chain)(store.dispatch)(action), 这里可以这样理解,每次dispatch的时候,中间件都是会执行一次,传入顺序是[logger, crashReport], 执行顺序为 crashReport -> logger, 自右向左执行。在每个中间件执行过程中都需要返回next(action) 将当前action 继续传递下去

其他参考: redux applyMiddleware 解析

dispatch(action)

下面说到dispatch,这是我们经常用的,如下 代码 :

function dispatch(action) {
    // 其他代码

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action) // ①
    } finally {
      isDispatching = false
    }

    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener() // ②
    }

    return action
  }

这里先说一下 isDispatching 作用, isDispatching 执行reducer 判断

isDispatching really needed?

这里这个参数 解决情况 如下:

var store = createStore((state={}, action) => {
  if (something) {
    store.dispatch({type: 'ANOTHER_ACTION'})
  }
  return state
})

继续下面来说在 ① 的位置执行 currentReducer, 这里reducer 为我们通过 createStore 传入combineReducers, 把对应的currentState 和 action传入, currentState 也是在初始阶段传入的 preloadState。在 ② 的位置则进行触发监听器, 监听器设置则在 store.subscribe 中增加。

现在来解释,action 和 reducer 对应关系

我当时回答的是: action.type 对应 reducer 的函数名称,如果一致的话就会执行。呵呵,那个涂鸦大工程师有问题了如何绑定的呢,怎么确定是reducerA 而不是B。 呵呵呵呵呵 。NB

后续

还问我 redux-saga、redux-thunk 异步处理方案,我提了一下redux-thunk,后续继续更新。

PS:真应该提升整个沟通流程质量

1.参考地址: https://redux.js.org


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

How to Solve It

How to Solve It

Zbigniew Michalewicz、David B. Fogel / Springer / 2004-03-01 / USD 59.95

This book is the only source that provides comprehensive, current, and detailed information on problem solving using modern heuristics. It covers classic methods of optimization, including dynamic pro......一起来看看 《How to Solve It》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具