是否可以使用 React 中的 useState() 挂钩在组件之间共享状态?

新手上路,请多包涵

我正在试验 React 中的新 Hook 特性。考虑到我有以下两个组件(使用 React Hooks)-

 const HookComponent = () => {
  const [username, setUsername] = useState('Abrar');
  const [count, setState] = useState();
  const handleChange = (e) => {
    setUsername(e.target.value);
  }

  return (
    <div>
      <input name="userName" value={username} onChange={handleChange}/>
      <p>{username}</p>
      <p>From HookComponent: {count}</p>
    </div>
  )
}

const HookComponent2 = () => {
  const [count, setCount] = useState(999);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Hooks 声称可以解决组件之间共享状态逻辑的问题,但我发现 HookComponentHookComponent2 之间的状态不可共享。例如,在 --- 中更改 count HookComponent2 不会在 HookComponent 中呈现更改。

是否可以使用 useState() 挂钩在组件之间共享状态?

原文由 Abrar 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 688
2 个回答

如果您指的是组件状态,那么挂钩将无法帮助您在组件之间共享它。组件状态是组件本地的。如果您的状态存在于上下文中,那么 useContext 挂钩会有所帮助。

从根本上说,我认为您误解了“在组件之间共享有状态逻辑”这一行。状态逻辑不同于状态。有状态逻辑是您所做的修改状态的事情。例如,一个组件订阅了 componentDidMount() 中的商店,并取消订阅了 componentWillUnmount() 中的商店。这种订阅/取消订阅行为可以在钩子中实现,需要这种行为的组件可以直接使用钩子。

如果你想在组件之间共享状态,有多种方法可以做到,每种方法都有自己的优点:

1. 提升状态

将状态提升到两个组件的共同祖先组件。

 function Ancestor() {
    const [count, setCount] = useState(999);
    return <>
      <DescendantA count={count} onCountChange={setCount} />
      <DescendantB count={count} onCountChange={setCount} />
    </>;
  }

这种状态共享方式与传统的状态使用方式并没有根本的区别,钩子只是给了我们一种不同的方式来声明组件状态。

2. 语境

如果后代在组件层次结构中太深并且您不想将状态向下传递太多层,则可以使用 Context API

有一个 useContext 挂钩,您可以在子组件中利用它。

3. 外部状态管理方案

状态管理库,如 Redux 或 Mobx。然后你的状态将存在于 React 之外的商店中,组件可以连接/订阅商店以接收更新。

原文由 Yangshun Tay 发布,翻译遵循 CC BY-SA 4.0 许可协议

没有任何外部状态管理库是可能的。只需使用一个简单的 可观察 实现:

 function makeObservable(target) {
  let listeners = []; // initial listeners can be passed an an argument aswell
  let value = target;

  function get() {
    return value;
  }

  function set(newValue) {
    if (value === newValue) return;
    value = newValue;
    listeners.forEach((l) => l(value));
  }

  function subscribe(listenerFunc) {
    listeners.push(listenerFunc);
    return () => unsubscribe(listenerFunc); // will be used inside React.useEffect
  }

  function unsubscribe(listenerFunc) {
    listeners = listeners.filter((l) => l !== listenerFunc);
  }

  return {
    get,
    set,
    subscribe,
  };
}

然后创建一个商店并通过在 useEffect subscribe 来挂钩它以做出反应:

 const userStore = makeObservable({ name: "user", count: 0 });

const useUser = () => {
  const [user, setUser] = React.useState(userStore.get());

  React.useEffect(() => {
    return userStore.subscribe(setUser);
  }, []);

  const actions = React.useMemo(() => {
    return {
      setName: (name) => userStore.set({ ...user, name }),
      incrementCount: () => userStore.set({ ...user, count: user.count + 1 }),
      decrementCount: () => userStore.set({ ...user, count: user.count - 1 }),
    }
  }, [user])

  return {
    state: user,
    actions
  }
}

这应该有效。不需要 React.Context 或提升状态

原文由 streletss 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题