React通过useSyncExternalStore实现一个简单的Redux

1,067 阅读1分钟

useSyncExternalStore

useSyncExternalStore 是一个让你订阅外部 store 的 React Hook。

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

我们在react中,实现数据共享,无非是三种方式

  • props传递或者useContext共享节点内的所有状态
  • 使用redux等状态管理库实现全局状态管理
  • 通过Event Bus去实现发布订阅

redux或者类似于zustand这种状态管理库他们是如何实现当状态更新而去重新渲染页面的呢?

在它们内部基本上都会维护两个状态,一个是currentState数据源,一个是listeners监听器数组。当我们订阅的时候,会去给监听器数组添加一个监听,当我们去修改数据的时候,再去循环执行监听器的函数,而这个函数就是我们需要想办法去重新render这个组件。 而使用了useSyncExternalStore这个hooks,就相当于为我们写好了这个监听器的函数。

代码实现

export function CreateStore(reducer: any) {
  let currentState = null;
  const listeners = [];

  function getSnapshot() {
    return currentState;
  }

  function dispatch(action) {
    currentState = reducer(currentState, action);
    listeners.forEach((listener) => listener());
  }

  function subscribe(listener) {
    listeners.push(listener);
  }

  return {
    getSnapshot,
    dispatch,
    subscribe,
  };
}

export function useStore() {
  const storeRef = useRef();

  if (!storeRef.current) {
    storeRef.current = CreateStore(countReducer);
  }

  return storeRef.current;
}

function countReducer(state = 0, action) {
  console.log(state, action);
  switch (action.type) {
    case "ADD":
      return state + 1;
    case "MINUS":
      return state - 1;
    default:
      return state;
  }
}

外部使用

import { Button } from "@nextui-org/react";
import { useStore } from "../../store/createStore";
import { useSyncExternalStore } from "react";

function Index() {
  const store = useStore();
  const state = useSyncExternalStore(store.subscribe, store.getSnapshot);

  console.log(state);
  return (
    <div className="flex flex-col gap-y-10 items-center justify-center">
      <Button onClick={() => store.dispatch({ type: "ADD" })}>Add</Button>
      <Button onClick={() => store.dispatch({ type: "MINUS" })}>MINUS</Button>
      {state}
    </div>
  );
}

export default Index;