理解redux中createStore背后的原理

在介绍 createStore 方法原理以前,先回顾一下 redux 的使用过程,而后再根据使用的方式编写源码。react

import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store
复制代码

使用 redux 经过 createStore 建立容器,须要传入一个函数 reducer 称为处理器,这个函数定义怎样修改容器里面的状态。redux

const initState = { number: 0 }

function reducer(state = initState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { number: state.number + 1 }
    case 'DECREMENT':
      return { number: state.number - 1 }
    default:
      return state
  }
}
export default reducer
复制代码

处理器函数第一个参数是要管理的状态,第二个参数是动做对象,那么接下来咱们开始定义这个动做对象。函数

const actions = {
  increment () {
    return {type: 'INCREMENT'}
  },
  decrement () {
    return {type: 'DECREMENT'}
  }
}

export default actions
复制代码

actions 能够写成对象和函数的形式,若是写成对象,那么对象中的每一项必须是函数。函数的返回值是对象,并且对象必需要有type属性。关于actions为何要这样写了,目的是能够提供给 bindActionCreators 函数使用。ui

function bindActionCreator(actionCreator, dispatch) {
  return (...args) => dispatch(actionCreator(...args))
}

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error('actionCreators是对象或函数')
  }

  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
}
复制代码

如今一切准备就绪,在组件中开始使用管理的状态,须要导入咱们建立的 store 容器和定义的 actions 动做,这里并无使用 react-redux,目的是为了演示 redux 的原始方法用法,为了更好的理解状态的更改的过程。this

import store from './store'
import actions from './store/actions'

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      number: 0
    }
  }

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
      this.setState({number: store.getState().number})
    })
  }
  componentWillUnmount () {
    this.unsubscribe()
  }
  handleClick = () => {
    store.dispatch(actions.increment())
  }
  render() { 
    return (
      <div> <p>{this.state.number}</p> <button onClick={this.handleClick}>+</button> </div>
    );
  }
}
复制代码

上面代码就是一个计数器组件,经过点击加号,会触发一个点击事件,点击事件会调用 dispatch 方法,传入参数是actions定义的动做类型,并且经过生命周期在组件挂载完成的在 subscribe 订阅了更新状态的方法,这样每次才能触发视图更新。spa

如今已经把 redux 的基本使用介绍完成,下面是实现内部的方法。code

export const ActionTypes = {
  INIT: '@@redux/INIT'
}

export default function createStore(reducer, preloadedState) {
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    let isSubscribed = true
    nextListeners.push(listener)
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      isSubscribed = false
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }


  function dispatch(action) {
    if (isDispatching) {
      throw new Error('reducer函数中不能调用dispatch')
    }

    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
  }

  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState
  }
}
复制代码

currentState 是容器的状态,在容器的建立时指定第二个参是默认状态,每次调用 dispatch 会触发更改状态的处理器函数 currentReducer 获得新的状态,而后会触发订阅更新视图的方法 listener 函数。component

相关文章
相关标签/搜索