【发布时间】: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 调用更复杂时,我建议将其表示为带有
useReducerHook 的本地状态归约器。
所以也许组合状态对象或useReducer 是一种方式。
【问题讨论】:
标签: reactjs react-hooks