像素

react-redux 源码学习

Provider

Provider 组件的作用是把 Redux store 实例提供给它的所有子组件,通过 Context 的特点对所有订阅的组件提供 stroe

function Provider<A extends Action = AnyAction, S = unknown>({
  store,
  context,
  children,
  serverState,
  stabilityCheck = 'once',
  noopCheck = 'once',
}: ProviderProps<A, S>) {
  // 1. 创建 context 上下文
  const contextValue = useMemo(() => {
    const subscription = createSubscription(store)
    return {
      store,
      subscription,
      getServerState: serverState ? () => serverState : undefined,
      stabilityCheck,
      noopCheck,
    }
  }, [store, serverState, stabilityCheck, noopCheck])

  // 2.获取初始state
  const previousState = useMemo(() => store.getState(), [store])

  // 4. useEffect 中创建订阅,当store更新触发订阅逻辑
  useIsomorphicLayoutEffect(() => {
    const { subscription } = contextValue
    subscription.onStateChange = subscription.notifyNestedSubs
    subscription.trySubscribe()

    if (previousState !== store.getState()) {
      subscription.notifyNestedSubs()
    }
    return () => {
      subscription.tryUnsubscribe()
      subscription.onStateChange = undefined
    }
  }, [contextValue, previousState])

  // 3.创建 context  ReactReduxContext为默认上下文
  const Context = context || ReactReduxContext

  // @ts-ignore 'AnyAction' is assignable to the constraint of type 'A', but 'A' could be instantiated with a different subtype
  return <Context.Provider value={contextValue}>{children}</Context.Provider>
}

useSelector

useSelector 由工厂函数 createSelectorHook 创建

function createSelectorHook (context = ReactReduxContext) {
  //1.创建 useReduxContext 钩子函数获取 contextValue
  const useReduxContext =
    context === ReactReduxContext
    ? useDefaultReduxContext
    : createReduxContextHook(context)
  // 2.返回useSelector
  return function useSelector(){};
}

useSelector 函数内部主要工作为四部分

  1. 初始化利用的参数,对传入的 selector 函数进行检查
  2. 调用 useReduxContext 获取 contextValue
  3. 对传入的selector进行包装成wrappedSelector ,利用 useCallback 缓存
  4. 执行 useSyncExternalStoreWithSelector 方法得到 selector 选择的结果 进行返回
function useSelector () {
  ...略

  // 3.包装 select 函数
    const wrappedSelector = useCallback<typeof selector>(
      {
        [selector.name](state: TState) {
          const selected = selector(state)
          //...
          return selected
        },
      }[selector.name],
      [selector, globalStabilityCheck, stabilityCheck]
    )
  
  // 4.内部执行 useSyncExternalStore
  // 通过 subscription.addNestedSub 订阅 wrappedSelector 执行结果形成快照
  // 返回 对应的 selectedState
  const selectedState = useSyncExternalStoreWithSelector(
    subscription.addNestedSub,
    store.getState,
    getServerState || store.getState,
    wrappedSelector,
    equalityFn
  );
  useDebugValue(selectedState)
  // 5. 返回 selectedState
  return selectedState
}

useSyncExternalStoreWithSelector

useSyncExternalStoreWithSelector 中对传入的 wrappedSelector 的结果包装成 getSnapshot 快照传入 useSyncExternalStore中。执行得到结果返回,当 getSnapshot 返回结果也即 selector 返回结果变更时(Object.is进行比较),会触发组件重新渲染。

export function useSyncExternalStoreWithSelector<Snapshot, Selection>(
  subscribe: (() => void) => () => void,
  getSnapshot: () => Snapshot,
  getServerSnapshot: void | null | (() => Snapshot),
  selector: (snapshot: Snapshot) => Selection,
  isEqual?: (a: Selection, b: Selection) => boolean,
  ): Selection {

    //...
    // 1.包装 selector => getSnapshot
    const [getSelection, getServerSelection] = useMemo(() => {
  // 使用getSnapshot函数的这个记忆实例的局部闭包变量跟踪记忆状态。
  // 故意不使用useRef钩子,因为该状态会被钩子/组件的所有并发副本共享。
  let hasMemo = false;
  let memoizedSnapshot;
  let memoizedSelection: Selection;
  // 对传入的 selector 进行包装记忆
  const memoizedSelector = (nextSnapshot: Snapshot) => {
    if (!hasMemo) {
      // 第一次调用钩子时,没有记住结果。
      hasMemo = true;
      memoizedSnapshot = nextSnapshot;
      const nextSelection = selector(nextSnapshot); // 执行 selector 获取结果
      //...
      memoizedSelection = nextSelection;
      return nextSelection;
    }
  };

  const maybeGetServerSnapshot =
    getServerSnapshot === undefined ? null : getServerSnapshot;
  // 包装 selector 成 getSnapshot 参数传入 useSyncExternalStore 中
  const getSnapshotWithSelector = () => memoizedSelector(getSnapshot());
  //...
  return [getSnapshotWithSelector, getServerSnapshotWithSelector];
}, [getSnapshot, getServerSnapshot, selector, isEqual]);

// 2. 执行useSyncExternalStore 获取结果
const value = useSyncExternalStore(
  subscribe,
  getSelection, // selector 获取的结果 作为快照传递给 useSyncExternalStore
  getServerSelection, // 服务端使用
);

//...
return value;
}

useSelector 的工作流程总结:

  1. 接收一个 selector 函数
  2. 内部调用 useSyncExternalStore, 传入这个 selector 获取的结果作为快照
  3. useSyncExternalStore 会订阅 Redux store 的变化,也就是之前传入快照的变化
  4. 当快照 发生变化时,useSyncExternalStore 会调用传入的getSnapshot 也就是这里的 getSelection。同时触发 React 的重新渲染。
  5. 将最新 selector 结果作为值返回

useDispatch

通过 createDispatchHook 创建

function createDispatchHook(context = ReactReduxContext) {
  const useStore =
    // @ts-ignore
    context === ReactReduxContext ? useDefaultStore : createStoreHook(context)

  return function useDispatch() {
    // 1.返回 stroe 中的 dispatch
    const store = useStore()
    return store.dispatch
  }
}

useStore

与useDispatch类似 通过createStoreHook创建

function createStoreHook(context = ReactReduxContext) {
  const useReduxContext =
    context === ReactReduxContext
    ? useDefaultReduxContext
    : createReduxContextHook(context)
  return function useStore() {
    // 返回contextValue 中的 store
    const { store } = useReduxContext()
    return store
  }
}