理解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

相關文章
相關標籤/搜索