Redux源码解析(1)

121 阅读3分钟

关注不迷路→Redux源码解析(2)

春暖花开,万物复苏,redux源码作者也在马不停蹄发5.0.0-alpha.2(截止2023.03),借着这个明媚的季节,让我们探索下redux的源码。

众所周知,redux集成许多状态管理库的优秀思想,“可预测数据变化”、“三大原则”等等众多概念令人眼花缭乱、望而却步,但是翻看源码其实非常短小精悍。

本系列分为两部分:

  1. 从createStore用法开始探讨state/action/dispatch/subscribe运转流程(本篇)
  2. 讨论applyMiddleware api到中间件运转流程(下一篇)

安装使用

通过CRA安装项目,并通过yarn add redux安装依赖,在App.tsx写入如下

import React, { useEffect, useRef } from "react";
import "./App.css";
import { type Store, createStore } from "redux";

interface Iaction {
  type: string;
}

function App() {
  const storeRef: React.MutableRefObject<Store | null> = useRef(null);

  useEffect(() => {
    const counterReducer = (state = { value: 0 }, action: Iaction) => {
      switch (action.type) {
        case "counter/incremented":
          return { value: state.value + 1 };
        case "counter/decremented":
          return { value: state.value - 1 };
        default:
          return state;
      }
    };
    storeRef.current = createStore(
      counterReducer
    );

    storeRef.current.subscribe(() => {
      console.log(storeRef.current?.getState());
    });
  });

  return (
    <div className="App">
      <button
        onClick={() =>
          storeRef.current?.dispatch({ type: "counter/incremented" })
        }
      >
        ADD
      </button>
    </div>
  );
}

export default App;

页面效果如下:

image.png

以上代码包含redux的最基本要素(state、action、reducer),流程也很清晰:

  1. 声明一个结合state和action的reducer函数,传入createStore函数生成一个Store类型对象
  2. 通过Store类型对象的subscribe方法提供的回调函数进行事件监听
  3. Store类型对象dispatch,监听事件生效

源码解析

进入src/createStore.ts,进入主函数,可以看到三个入参reducer、 preloadedState、enhancer,enhancer与中间件有关先不细说。源码解析如下:

export function createStore<
S,
A extends Action,
Ext extends {} = {},
StateExt extends {} = {}
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<S, A, StateExt> & Ext {
if (typeof reducer !== 'function') {
  throw new Error(
    `Expected the root reducer to be a function. Instead, received: '${kindOf(
      reducer
    )}'`
  )
}

if (
  (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
  (typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
  throw new Error(
    'It looks like you are passing several store enhancers to ' +
      'createStore(). This is not supported. Instead, compose them ' +
      'together to a single function. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
  )
}

if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
  enhancer = preloadedState as StoreEnhancer<Ext, StateExt>
  preloadedState = undefined
}

if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error(
      `Expected the enhancer to be a function. Instead, received: '${kindOf(
        enhancer
      )}'`
    )
  }

  return enhancer(createStore)(
    reducer,
    preloadedState as PreloadedState<S>
  ) as Store<S, A, StateExt> & Ext
}

// 以上为防御性代码和进入中间件处理逻辑,略

// 当前reducer
let currentReducer = reducer
// 当前数据
let currentState = preloadedState as S
// 监听事件集合
let currentListeners: Map<number, ListenerCallback> | null = new Map()
// 监听事件副本
let nextListeners = currentListeners
// 监听事件id
let listenerIdCounter = 0
// 是否正在执行state的修改,作为一个锁,防止多个dispatch一起工作
let isDispatching = false

function ensureCanMutateNextListeners() {
  if (nextListeners === currentListeners) {
    nextListeners = new Map()
    currentListeners.forEach((listener, key) => {
      nextListeners.set(key, listener)
    })
  }
}

// 获取总的数据
function getState(): S {
  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 as S
}

// 订阅事件
function subscribe(listener: () => void) {
  if (typeof listener !== 'function') {
    throw new Error(
      `Expected the listener to be a function. Instead, received: '${kindOf(
        listener
      )}'`
    )
  }
  // 检测是否并发
  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/store#subscribelistener for more details.'
    )
  }
  // 防止多次触发取消订阅
  let isSubscribed = true

  ensureCanMutateNextListeners()
  const listenerId = listenerIdCounter++
  // 并入事件集合
  nextListeners.set(listenerId, 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/store#subscribelistener for more details.'
      )
    }

    isSubscribed = false

    ensureCanMutateNextListeners()
    nextListeners.delete(listenerId)
    currentListeners = null
  }
}

function dispatch(action: A) {
  if (!isPlainObject(action)) {
    throw new Error(
      `Actions must be plain objects. Instead, the actual type was: '${kindOf(
        action
      )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
    )
  }

  if (typeof action.type === 'undefined') {
    throw new Error(
      'Actions may not have an undefined "type" property. You may have misspelled an action type string 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)
  listeners.forEach(listener => {
    listener()
  })
  // 中间件执行需要透传的value
  return action
}

function replaceReducer(nextReducer: Reducer<S, A>): void {
  if (typeof nextReducer !== 'function') {
    throw new Error(
      `Expected the nextReducer to be a function. Instead, received: '${kindOf(
        nextReducer
      )}`
    )
  }
  // 替换reducer
  currentReducer = nextReducer

  // 预埋事件
  dispatch({ type: ActionTypes.REPLACE } as A)
}
// 适配rxjs之类的库的操作
function observable() {
  const outerSubscribe = subscribe
  return {
    subscribe(observer: unknown) {
      if (typeof observer !== 'object' || observer === null) {
        throw new TypeError(
          `Expected the observer to be an object. Instead, received: '${kindOf(
            observer
          )}'`
        )
      }

      function observeState() {
        const observerAsObserver = observer as Observer<S>
        if (observerAsObserver.next) {
          observerAsObserver.next(getState())
        }
      }

      observeState()
      const unsubscribe = outerSubscribe(observeState)
      return { unsubscribe }
    },

    [$$observable]() {
      return this
    }
  }
}

// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT } as A)

const store = {
  dispatch: dispatch as Dispatch<A>,
  subscribe,
  getState,
  replaceReducer,
  [$$observable]: observable
} as unknown as Store<S, A, StateExt> & Ext
return store
}

总结以上的代码流程:

  1. 预先存储数据(state)和对数据的分类处理方法(reducer)

  2. 执行subscribe存储(或多次)订阅事件

  3. dispatch函数触发reducer,修改state,执行存储的订阅事件,无入参