React:这是实现共享状态订阅的好方法吗?

Joj*_*oji 7 javascript reactjs

不确定这是所谓的“发布/订阅”模式还是“发布/订阅”模式的一种形式。我正在尝试创建一个共享状态,以便不同的组件可以订阅它,并且只有在该状态有更新时才会更新。

const useForceUpdate = () => useReducer((state) => !state, false)[1];

const createSharedState = (reducer, initialState) => {
  const subscribers = [];
  let state = initialState;
  const dispatch = (action) => {
    state = reducer(state, action);
    subscribers.forEach((callback) => callback());
  };
  const useSharedState = () => {
    const forceUpdate = useForceUpdate();
    useEffect(() => {
      const callback = () => forceUpdate();
      subscribers.push(callback);
      const cleanup = () => {
        const index = subscribers.indexOf(callback);
        subscribers.splice(index, 1);
      };
      return cleanup;
    }, []);
    return [state, dispatch];
  };
  return useSharedState;
};

const initialState = 0;
const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    case "set":
      return action.count;
    default:
      return state;
  }
};

const useCount1 = createSharedState(reducer, initialState);
const useCount2 = createSharedState(reducer, initialState);

const Counter = ({ count, dispatch }) => (
  <div>
    {count}
    <button onClick={() => dispatch({ type: "increment" })}>+1</button>
    <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
    <button onClick={() => dispatch({ type: "set", count: 0 })}>reset</button>
  </div>
);

const Counter1 = () => {
  const [count, dispatch] = useCount1();
  return <Counter count={count} dispatch={dispatch} />;
};

const Counter2 = () => {
  const [count, dispatch] = useCount2();
  return <Counter count={count} dispatch={dispatch} />;
};

const Example = () => (
  <>
    <Counter1 />
    <Counter1 />
    <Counter2 />
    <Counter2 />
  </>
);
Run Code Online (Sandbox Code Playgroud)

<script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>
<div id="root"></div>

<script type="text/babel">
const { useEffect, useReducer } = React;

const useForceUpdate = () => useReducer((state) => !state, false)[1];

const createSharedState = (reducer, initialState) => {
  const subscribers = [];
  let state = initialState;
  const dispatch = (action) => {
    state = reducer(state, action);
    subscribers.forEach((callback) => callback());
  };
  const useSharedState = () => {
    const forceUpdate = useForceUpdate();
    useEffect(() => {
      const callback = () => forceUpdate();
      subscribers.push(callback);
      const cleanup = () => {
        const index = subscribers.indexOf(callback);
        subscribers.splice(index, 1);
      };
      return cleanup;
    }, []);
    return [state, dispatch];
  };
  return useSharedState;
};

const initialState = 0;
const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    case "set":
      return action.count;
    default:
      return state;
  }
};

const useCount1 = createSharedState(reducer, initialState);
const useCount2 = createSharedState(reducer, initialState);

const Counter = ({ count, dispatch }) => (
  <div>
    {count}
    <button onClick={() => dispatch({ type: "increment" })}>+1</button>
    <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
    <button onClick={() => dispatch({ type: "set", count: 0 })}>reset</button>
  </div>
);

const Counter1 = () => {
  const [count, dispatch] = useCount1();
  return <Counter count={count} dispatch={dispatch} />;
};

const Counter2 = () => {
  const [count, dispatch] = useCount2();
  return <Counter count={count} dispatch={dispatch} />;
};

const Example = () => (
  <>
    <Counter1 />
    <Counter1 />
    <Counter2 />
    <Counter2 />
  </>
);

ReactDOM.render(<Example />, document.querySelector("#root"));
</script>
Run Code Online (Sandbox Code Playgroud)

它似乎工作正常。我的问题是:

  1. 这是实现共享更新订阅的有效方法吗?
  2. 使用一个简单的变量来保存状态 + 如果该状态发生变化则强制 React 重新渲染,而不是使用useStateuseReducer通常会做的事情,是否有任何缺点?
  3. 欢迎任何反馈。

tei*_*jan 6

  1. 你的想法很棒。React 团队也在考虑这个话题,最终创建了https://recoiljs.org/。您可以将其用作useState( DEMO ) 或useReducer( DEMO )。
  2. 我不想强调您的解决方案的缺点。相反,我想列出使用 Recoil 的优点:
  • 内部内存使用优化。
  • 无需支持代码(Facebook 做到了)。
  • 没有作弊(useForceUpdate)。
  • 支持开箱即用的选择器。
  1. 我建议您了解有关 Recoil 的更多信息并开始使用它,因为它可以提供您想要达到的确切结果。