我可以在一个使用有效的钩子内设置状态吗?

2022-08-30 00:47:39

假设我有一些状态依赖于其他状态(例如,当A改变时,我希望B改变)。

创建一个观察 A 并将 B 设置在 useEffect 钩子内的钩子是否合适?

效果是否会级联,以至于当我单击按钮时,第一个效果将触发,导致 b 发生变化,从而导致第二个效果触发,然后再进行下一次渲染?像这样构建代码有什么性能缺点吗?

let MyComponent = props => {
  let [a, setA] = useState(1)
  let [b, setB] = useState(2)
  useEffect(
    () => {
      if (/*some stuff is true*/) {
        setB(3)
      }
    },
    [a],
  )
  useEffect(
    () => {
      // do some stuff
    },
    [b],
  )

  return (
    <button
      onClick={() => {
        setA(5)
      }}
    >
      click me
    </button>
  )
}

答案 1

一般来说,使用inside会创建一个无限循环,很可能你不想引起。该规则有几个例外,我稍后将对此进行介绍。setStateuseEffect

useEffect在每次渲染后调用,当在其中使用时,它将导致组件重新呈现,这将调用等等。setStateuseEffect

使用 inside 不会导致无限循环的一种流行情况是,当您将空数组作为第二个参数传递给 like 时,这意味着 effect 函数应该调用一次:仅在第一次装载/渲染之后。当您在组件中执行数据提取并且希望将请求数据保存在组件的状态中时,这被广泛使用。useStateuseEffectuseEffectuseEffect(() => {....}, [])


答案 2

对于将来的目的,这可能也会有所帮助:

使用 setState 是可以的,你只需要注意,如前所述,不要创建循环。useEffect

但这并不是唯一可能发生的问题。见下文:

想象一下,你有一个从父级接收的组件,并根据你想要设置的状态的更改。出于某种原因,您需要在不同的属性中更改每个道具:ComppropspropsCompuseEffect

不要这样做

useEffect(() => {
  setState({ ...state, a: props.a });
}, [props.a]);

useEffect(() => {
  setState({ ...state, b: props.b });
}, [props.b]);

它可能永远不会更改 a 的状态,如您在此示例中看到的那样:https://codesandbox.io/s/confident-lederberg-dtx7w

在此示例中发生这种情况的原因是,当您更改两者时,两者都运行在同一个反应周期中,因此当您这样做时,它们的值在两者中完全相同,因为它们位于相同的上下文中。当您运行第二个时,它将替换第一个 .prop.aprop.b{...state}setStateuseEffectsetStatesetState

请改为执行此操作

这个问题的解决方案基本上是这样调用的:setState

useEffect(() => {
  setState(state => ({ ...state, a: props.a }));
}, [props.a]);

useEffect(() => {
  setState(state => ({ ...state, b: props.b }));
}, [props.b]);

在此处查看解决方案:https://codesandbox.io/s/mutable-surf-nynlx

现在,当您继续执行 时,您始终会收到状态的更新和正确的值。setState

我希望这对某人有所帮助!