【问题标题】:Updating state based on multiple other state values using hooks使用钩子基于多个其他状态值更新状态
【发布时间】:2020-09-14 20:13:00
【问题描述】:

异步状态更新和类组件

我们在 React 状态和生命周期文档 that state updates may be asynchronous 中收到警告。因此,我们不应该依赖它们的值来计算下一个状态:

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

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

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

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

函数组件和useState

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

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

大概同样的异步规则适用,这就是为什么useState钩子提供的setA函数也接受一个函数参数来更新它,如下所示:

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

根据函数组件中的多个其他状态值更新状态

当我们想根据b 的值更新a 时会发生什么?基于之前状态更新是异步的问题,我们不想直接引用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 Hook 的本地状态归约器。

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

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    这就是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

    【讨论】:

    • 感谢您的回复。有趣的方法,我没想过将useEffect 与依赖数组一起使用来触发依赖状态更新。当您没有设置您也引用的某些状态时,这似乎确实有效。但是在我上面给出的示例中,其中 a = a + b 我认为这不起作用,因为它会无限递归。您是否倾向于在真实代码中处理这样的依赖状态更新?还是在共享对象方法上使用依赖状态?
    猜你喜欢
    • 1970-01-01
    • 2020-01-01
    • 2014-09-28
    • 1970-01-01
    • 2021-07-17
    • 1970-01-01
    • 2021-06-02
    • 2019-10-29
    • 1970-01-01
    相关资源
    最近更新 更多