美文网首页redux
redux 源码 试读

redux 源码 试读

作者: 别过经年 | 来源:发表于2017-10-27 16:13 被阅读9次

    shopping-cart

    src/index.js

    import React from 'react'
    import { render } from 'react-dom'
    import { createStore, applyMiddleware } from 'redux'
    import { Provider } from 'react-redux'
    import { createLogger } from 'redux-logger'
    import thunk from 'redux-thunk'
    import reducer from './reducers'
    import { getAllProducts } from './actions'
    import App from './containers/App'
    
    const middleware = [ thunk ];
    if (process.env.NODE_ENV !== 'production') {
      middleware.push(createLogger());
    }
    
    const store = createStore(
      reducer,
      applyMiddleware(...middleware)
    )
    
    store.dispatch(getAllProducts())
    
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    
    

    actions/index.js

    import shop from '../api/shop'
    import * as types from '../constants/ActionTypes'
    
    const receiveProducts = products => ({
      type: types.RECEIVE_PRODUCTS,
      products: products
    })
    
    //这个很有意思返回的居然不是上面的{type:''}对象,而是个函数
    export const getAllProducts = () => dispatch => {
      shop.getProducts(products => {
        dispatch(receiveProducts(products))
      })
    }
    

    getAllProducts action返回的是函数,匪夷所思,开始怀疑,是不是reducer第二个参数不是action单纯的对象,debug代码后才知道这和thunk中间件有关

    redux-thunk createStore.js

    第二张图可以清晰的看到action依旧是单纯的包含type属性的普通对象

    发现一小哥也是产生了同样的疑问由redux 异步数据流引发的血案

    1. bindActionCreators

    TodoActionCreators.js

    export function addTodo(text) {
      return {
        type: 'ADD_TODO',
        text
      };
    }
    
    export function removeTodo(id) {
      return {
        type: 'REMOVE_TODO',
        id
      };
    }
    

    SomeComponent.js

    // 这样做行得通:
        let action = TodoActionCreators.addTodo('Use Redux');
        dispatch(action);
    

    由此可见dispatch是个带有type属性的对象
    bindActionCreators源码:

    function bindActionCreator(actionCreator, dispatch) {
      return (...args) => dispatch(actionCreator(...args))
    }
    //参数可为单个actionCreator函数,也可以为多个actionCreator函数组成的对象
    export default function bindActionCreators(actionCreators, dispatch) {
      if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch)
      }
    
      if (typeof actionCreators !== 'object' || actionCreators === null) {
        throw new Error(
          `bindActionCreators expected an object or a function, instead received ${actionCreators === null ? 'null' : typeof actionCreators}. ` +
          `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
        )
      }
    
      const keys = Object.keys(actionCreators)
      const boundActionCreators = {}
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const actionCreator = actionCreators[key]
        if (typeof actionCreator === 'function') {
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
        }
      }
      return boundActionCreators
    }
    

    app.js

    <MainSection todos={todos} actions={actions} />
    
    const mapDispatchToProps = dispatch => ({
        actions: bindActionCreators(TodoActions, dispatch)
    })
    

    rodomvc mainsection.js

    //这样组件就直接调用函数了,而不需要再去dispatch一个actionCreator
     handleClearCompleted = () => {
        this.props.actions.clearCompleted()
      }
    

    2. action和请求是怎么建立的关系?也就是和reudcers怎么建立关系?

    今天在看todmvc实例的时候再次看到了combineReducers

    import { combineReducers } from 'redux'
    import todos from './todos'
    
    const rootReducer = combineReducers({
      todos
    })
    
    export default rootReducer
    

    然后带着疑问去查看redux文档Reducer逐层对reducer的拆分进行了讨论,最后就是将多个reducer函数使用combineReducers组合成了一个大的reducer函数,查阅redux createStore代码:

    export default function createStore(reducer, preloadedState, enhancer) {
      let currentReducer = reducer//唯一的reducer函数
      let currentState = preloadedState//初始全局state
    
      function dispatch(action) {
        try {
          isDispatching = true
          //调用注册好的reducer函数,action是包含type属性的纯对象,
          //currentReducer会返回新的state,从而达到更新state的目的
          //currentReducer会根据不同的action的type去更新对应的state
          //也就是在这里action和reducer建立了联系,reducer是在
          //createStore调用的时候传递进来的,见下文
          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
      }
    
      return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
      }
    }
    

    currentReducer就是被多个reducer函数组合后的函数,说白了就是使用switch去判断action.type更新对应的state
    createStore的调用

    import reducer from './reducers'
    //将组合后的reducer函数传到createStore内
    const store = createStore(reducer)
    
    render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    )
    

    简化版的redux 从0实现一个tinyredux说的比较清楚

    源码:

    export default function combineReducers(reducers) {
      const reducerKeys = Object.keys(reducers)
      const finalReducers = {}
      for (let i = 0; i < reducerKeys.length; i++) {
        const key = reducerKeys[i]
      }
      const finalReducerKeys = Object.keys(finalReducers)
    //返回一个拼装后的reducer
      return function combination(state = {}, action) {
        if (shapeAssertionError) {
          throw shapeAssertionError
        }
    
        if (process.env.NODE_ENV !== 'production') {
          const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
          if (warningMessage) {
            warning(warningMessage)
          }
        }
    
        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
    //这个地方循环判断前后两次局部state是不是同一个数据
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
        }
    //如果有一条局部state更新了,那么整个state都会是新的nextState,
    //如果全部的局部state都没更新,那么就返回原来的state,不对全局的state进行更新
        return hasChanged ? nextState : state
      }
    }
    

    combineReducers和没有使用该函数的前后对比,按照官网的说法
    你也可以给它们设置不同的 key,或者调用不同的函数。下面两种合成 reducer 方法完全等价

    const reducer = combineReducers({
      a: doSomethingWithA,
      b: processB,
      c: c
    })
    
    function reducer(state = {}, action) {
      return {
        a: doSomethingWithA(state.a, action),
        b: processB(state.b, action),
        c: c(state.c, action)
      }
    }
    

    3.state更新后react组件是怎么更新的?

    前面两条说了dispatch(action)对state进行更新,那么state更新了,react组件是怎么更新的呢?下面就这个问题展开讨论:

    相关文章

      网友评论

        本文标题:redux 源码 试读

        本文链接:https://www.haomeiwen.com/subject/zyfdpxtx.html