使用挂钩根据其他多个状态值更新状态

时间:2020-09-14 20:13:00

标签: reactjs react-hooks

异步状态更新和类组件

React State and Lifecycle文档that state updates may be asynchronous中警告我们。因此,我们不应依赖于它们的值来计算下一个状态:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment
})

相反,我们应该使用setState的函数更新形式来确保我们具有正确的state先前值:

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}))

此示例适用于可在函数回调中引用整个状态的类组件。

功能组件和useState

在定义函数组件时,我们可以为状态设置单独的值,如下所示:

function MyComponent() {
  const [a, setA] = useState(1)
  const [b, setB] = useState(2)
}

大概应用了相同的异步规则,这就是setA钩子提供的useState函数也接受函数参数更新的原因,就像这样:

function MyComponent() {
  const [a, setA] = useState(1)
  const [b, setB] = useState(2)
  const onClick = () => {
    setA(a => a + 1)
  }
  return <>
    <div>a: {a}</div>
    <div>b: {b}</div>
    <button onClick={onClick}>Update a</button>
  </>
}

基于功能组件中的其他多个状态值更新状态

当我们想基于a的值更新b时会发生什么?基于状态更新异步的早期问题,我们不想直接引用b,而是使用set state的函数版本来接收其值。

这可以通过将我们所有的状态放到这样的单个对象上来实现:

function MyComponent() {
  const [state, setState] = useState({ a: 1, b: 2 })
  const onClick = () => {
    setState(state => ({ b: state.b, a: state.a + state.b }))
  }
  return <>
    <div>a: {state.a}</div>
    <div>b: {state.b}</div>
    <button onClick={onClick}>Update a based on b</button>
  </>
}

这对我来说很有意义。

我的问题是:使用钩子时,使用如上所述的组合对象(或useReducer)是基于多个其他状态值更新状态值的唯一适当且安全的方法吗?

或者像这样通过闭包引用变量是否合适(我不正确):

function MyComponent() {
  const [a, setA] = useState(1)
  const [b, setB] = useState(2)
  const onClick = () => {
    setA(a => a + b)
  }
  return <>
    <div>a: {a}</div>
    <div>b: {b}</div>
    <button onClick={onClick}>Update a</button>
  </>
}

还是其他方式?


编辑:

更多的挖掘工作,我发现了Dan Abramov React UI as a Runtime - Batching的这篇博客文章。在其中,他建议使用useReducer处理复杂的状态更新情况:

当状态逻辑变得比几个setState调用复杂时,我建议使用useReducer挂钩将其表示为本地状态缩减器。

所以也许是组合状态对象或useReducer是这种方式。

1 个答案:

答案 0 :(得分:2)

这就是React.useEffect发挥作用的地方:

import React from "react";
import "./styles.css";

export default function App() {
  const [a, setA] = React.useState();
  const [b, setB] = React.useState();
  const [c, setC] = React.useState();

  React.useEffect(() => {
    setA(5);
    setB(5);
  }, []);

  React.useEffect(() => setC(a + b), [a, b]);

  return (
    <div className="App">
      <h1>{c}</h1>
    </div>
  );
}

正在渲染组件时,第一个React.useEffect设置a和b。 第二个React.useEffect根据a和b设置c,并且将更改以下依赖项之一(ab)时将触发它。

See live demo