Redux源码解析

138 阅读3分钟

redux的基本使用

1,创建reducers

2,用reducers创建一个store

3,组件使用store的subscribe订阅store,当store的状态修改的时候,会自动进行更新

4,组件通过store的dispatch派发action,从而达到修改store的状态

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'

let defaultState = {
  inputValue: 3
}
let reducers = (state = defaultState, action) => {
  let newState = Object.assign({}, state);
  if(action.type=='add') {
    newState.inputValue += 1;
    return newState;
  } else if (action.type=='decrease') {
    newState.inputValue -= 1;
    return newState;
  }
  return newState;
}

let store = createStore(reducers);

class App extends React.Component {
  constructor(){
    super()
    this.stateChange =  this.stateChange.bind(this);
    store.subscribe(this.stateChange);
    this.state={
      inputValue:0
    }
    
  }

  render() {
    return (
      <div>
        value:{this.state.inputValue}
           <button onClick={this.addChange}>加一加</button>
           <button onClick={this.decreaseChange}>剪一剪</button>
       
      </div>
    )
  }
  stateChange(){
    this.setState({
      inputValue:store.getState().inputValue
    })
  }

  addChange(){
    store.dispatch({
      type:"add",
      value:1
    })
  }

  decreaseChange(){
    store.dispatch({
      type:"decrease",
      value:1
    })
  }
}

ReactDOM.render(<App></App>,document.getElementById('root'))

reducx源码结构

src目录结构:utils下面是工具类

首先从工具类看起,其他核心模块会引用到工具类。

ActionTypes.js:

const randomString = () =>
  Math.random()
    .toString(36)
    .substring(7)
    .split('')
    .join('.')

const ActionTypes = {
  INIT: `@@redux/INIT${randomString()}`,
  REPLACE: `@@redux/REPLACE${randomString()}`,
  PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
}

export default ActionTypes

这个类很简单,定义了内部使用的action类型

isPlainObject.js:

export default function isPlainObject(obj) {
  if (typeof obj !== 'object' || obj === null) return false

  let proto = obj
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }

  return Object.getPrototypeOf(obj) === proto
}

定义一个判断对象是否是简单对象的方法。

warning.js:

export default function warning(message) {
  /* eslint-disable no-console */
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    console.error(message)
  }
}

定义一个通用的抛出错误警告的方法

index.js:

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  __DO_NOT_USE__ActionTypes
}

index是redux的入口,导出了redux所有对外暴露的api

createStore.js:

createStore(reducer, preloadedState, enhancer)

createStore有三个参数:

reducer:处理action的函数,接受action,返回新的state

preloadedState:是一个预置状态,很少用

enhancer:store的增强器,可以增强store的功能


  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

当createStore只传入两个参数的时候,会将第二个参数的值赋值给enhancer,所以,使用的时候可以直接省略第二个参数,直接使用enhancer

if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }

    return enhancer(createStore)(reducer, preloadedState)
  }

这里可以看到enhancer对createStore进行了封装增强

 let currentReducer = reducer  //当前的reducer
  let currentState = preloadedState //当前的state
  let currentListeners = []  //当前的订阅者
  let nextListeners = currentListeners //最新的订阅者
  let isDispatching = false //用来锁定state资源

isDispatching:用来锁定state

currentListeners和nextListeners主要是用来当正在通知的时候不会打乱当前的订阅者的顺序。

function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }

    return currentState
  }

当getState()的时候,会判断当前资源是不是在被占用,如果在占用,就会抛出异常。

function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }

    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }

      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
        )
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

首先判断订阅者传递的是不是一个函数,然后store中的资源有没有被占用。最后返回一个解除订阅者的函数

function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }

    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }

    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    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
  }

dipatch处理用户传入的action,reducer根据action的type对store中的state进行更改,返回新的state赋值给currentState.然后循环当前的所有订阅者,通知store的更新。