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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/53451584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 10:12:18  来源:igfitidea点击:

Is it possible to share states between components using the useState() hook in React?

javascriptreactjsreact-hooks

提问by Abrar

I was experimenting with the new Hook feature in React. Considering I have the following two components (using React Hooks) -

我正在尝试使用 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 claim to solve the problem of sharing stateful logic between components but I found that the states between HookComponentand HookComponent2are not sharable. For example the change of countin HookComponent2does not render a change in the HookComponent.

Hooks 声称解决了组件之间共享有状态逻辑的问题,但我发现HookComponent和之间的状态HookComponent2不可共享。例如,countin的变化HookComponent2不会呈现HookComponent.

Is it possible to share states between components using the useState()hook?

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

回答by Yangshun Tay

If you are referring to component state, then hooks will not help you share it between components. Component state is local to the component. If your state lives in context, then useContexthook would be helpful.

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

Fundamentally, I think you misunderstood the line "sharing stateful logic between components". Stateful logic is different from state. Stateful logic is stuff that you do that modifies state. For e.g., a component subscribing to a store in componentDidMount()and unsubscribing in componentWillUnmount(). This subscribing/unsubscribing behavior can be implemented in a hook and components which need this behavior can just use the hook.

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

If you want to share state between components, there are various ways to do so, each with its own merits:

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

1. Lift State Up

1. 提升状态

Lift state up to a common ancestor component of the two components.

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

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

This state sharing approach is not fundamentally different from the traditional way of using state, hooks just give us a different way to declare component state.

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

2. Context

2. 背景

If the descendants are too deep down in the component hierarchy and you don't want to pass the state down too many layers, you could use the Context API.

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

There's a useContexthook which you can leverage on within the child components.

useContext您可以在子组件中利用一个钩子。

3. External State Management Solution

3.外部状态管理解决方案

State management libraries like Redux or Mobx. Your state will then live in a store outside of React and components can connect/subscribe to the store to receive updates.

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

回答by pie6k

I've created hooksy that allows you to do exactly this - https://github.com/pie6k/hooksy

我已经创建了 hooksy,可以让你做到这一点 - https://github.com/pie6k/hooks

import { createStore } from 'hooksy';

interface UserData {
  username: string;
}

const defaultUser: UserData = { username: 'Foo' };

export const [useUserStore] = createStore(defaultUser); // we've created store with initial value.
// useUserStore has the same signature like react useState hook, but the state will be shared across all components using it

And later in any component

然后在任何组件中

import React from 'react';

import { useUserStore } from './userStore';

export function UserInfo() {
  const [user, setUser] = useUserStore(); // use it the same way like useState, but have state shared across any component using it (eg. if any of them will call setUser - all other components using it will get re-rendered with new state)

  function login() {
    setUser({ username: 'Foo' })
  }

  return (
    <div>
      {!user && <strong>You're logged out<button onPress={login}>Login</button></strong>}
      {user && <strong>Logged as <strong>{user.username}</strong></strong>}
    </div>
  );
}

回答by Karim

the docstates:

文档指出:

We import the useState Hook from React. It lets us keep local state in a function component.

我们从 React 导入 useState Hook。它让我们在函数组件中保持本地状态。

it's not mention that the state could be shared across components, useStatehook just give you a quicker way to declare a state field and its correspondent setter in one single instruction.

它没有提到可以跨组件共享状态,useStatehook 只是为您提供了一种在单个指令中声明状态字段及其对应设置器的更快方法。

回答by Yongzhi

You will still need to lift your state up to an ancestor component of HookComponent1 and HookComponent2. That's how you share state before and the latest hook api doesnt change anything about it.

您仍然需要将状态提升到 HookComponent1 和 HookComponent2 的祖先组件。这就是你之前共享状态的方式,最新的钩子 api 不会改变它的任何内容。

回答by Slava Bereza

This is possible using the useBetweenhook.

使用useBetween钩子可以做到这一点。

See in codesandbox

在代码和框中查看

import React, { useState } from 'react';
import { useBetween } from 'use-between';

const useShareableState = () => {
  const [username, setUsername] = useState('Abrar');
  const [count, setCount] = useState(0);
  return {
    username,
    setUsername,
    count,
    setCount
  }
}


const HookComponent = () => {
  const { username, setUsername, count } = useBetween(useShareableState);

  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 } = useBetween(useShareableState);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

We move React hooks stateful logic from HookComponentto useShareableState. We call useShareableStateusing useBetweenin each component.

我们将 React hooks 有状态逻辑从HookComponentuseShareableState。我们在每个组件中调用useShareableStateusing useBetween

useBetweenis a way to call any hook. But so that the state will not be stored in the React component. For the same hook, the result of the call will be the same. So we can call one hook in different components and work together on one state. When updating the shared state, each component using it will be updated too.

useBetween是一种调用任何钩子的方法。但是这样状态就不会存储在 React 组件中。对于同一个钩子,调用的结果是一样的。所以我们可以在不同的组件中调用一个钩子并在一种状态上协同工作。当更新共享状态时,使用它的每个组件也将被更新。

回答by streletss

It is possible without any external state management library. Just use a simple observableimplementation:

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

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,
  };
}

And then create a store and hook it to react by using subscribein useEffect:

然后创建一个 store 并使用subscribein钩住它以做出反应useEffect

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

And that should work. No need for React.Contextor lifting state up

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

回答by Marcel Ennix

With hooks its not directly possible. I recommend you to take a look at react-easy-state. https://github.com/solkimicreb/react-easy-state

使用钩子不是直接可能的。我建议你看一下 react-easy-state。 https://github.com/solkimicreb/react-easy-state

I use it in big Apps and it works like a charm.

我在大型应用程序中使用它,它就像一个魅力。