javascript - react : is this a good way to implement a shared state subscription?

标签 javascript reactjs

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

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 />
  </>
);

<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>

它似乎工作正常。我的问题是:
  • 这是实现共享更新订阅的有效方法吗?
  • 使用简单的变量来保存状态是否有任何缺点 + 如果状态发生更改,则强制 React 重新渲染,而不是使用 useStateuseReducer像一个通常会做的那样?
  • 欢迎任何反馈。
  • 最佳答案

  • 你的想法很棒。 React 团队也在思考这个话题,最终创建了 https://recoiljs.org/。 .您可以将其用作 useState ( DEMO ) 或 useReducer (DEMO)。
  • 我不想强调您的解决方案的缺点。相反,我想列出使用 Recoil 的优点:
  • 内部内存使用优化。
  • 无需支持代码(Facebook 支持)。
  • 没有作弊(useForceUpdate)。
  • 支持开箱即用的选择器。
  • 我建议您了解有关 Recoil 的更多信息并开始使用它,因为它可以提供您想要达到的确切结果。
  • 关于javascript - react : is this a good way to implement a shared state subscription?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66609157/

    相关文章:

    javascript - Chrome : Breakpoint when location. href 已设置

    javascript - React Router 子渲染问题

    javascript - 使提交 <button> 在 <form> block 之外工作?

    javascript - 我如何将数据(如用户 ID)传递给网络 worker 以从服务器获取额外的推送通知数据?

    javascript - 如何从指令访问 Controller 中的方法

    javascript - jQuery keyup 仅影响文本区域内容的键

    reactjs - 数组数据的初始状态在Reducer中未定义

    reactjs - Eslint-config-rallycoding 安装

    reactjs - 如何在 Material 表的右上角添加 "plus-button"以进行 react 并在 react 中调用我的特定功能

    css - 如何在 material ui REACTjs 中覆盖 menuItem 中的选定类?