美文网首页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