setState覆盖现有状态

时间:2019-09-04 06:48:14

标签: reactjs

我具有以下设置状态的功能:

const [study, setStudy] = useState(defaultState)

const setValue = (section, key, value) => {
  setStudy({...study, [section]: {...study[section], [key]: value}})
}

但是由于某种原因,它会继续覆盖现有状态,我在做什么错了?

我这样调用函数:

setValue('company', 'name', 'test')
setValue('property', 'state', 'test2')
setValue('property', 'address', 'test3')

数据结构:

const defaultState: StudyData = {
  client: {},
  company: {},
  property: {},
  study: {}
}

2 个答案:

答案 0 :(得分:2)

我认为问题在于您破坏的学习价值仍然是旧版本(被记忆)。所有setValue调用之间都是相同的,因此前两个setValue调用实际上什么也不做。如果这是您的情况,则解决此问题的最佳方法是为setStudy提供回调函数。给定值器的任何函数都将获取状态的绝对最新版本作为参数。像这样尝试:

setStudy((latestStudy) => {
    return {
        ...latestStudy,
        [section]: {...latestStudy[section], [key]: value}
    }
});

答案 1 :(得分:1)

在处理多个状态更新事件时,React的标准行为是对它们进行批处理。状态更新方法不会立即更新组件的状态,React只是将更新放入队列中,以便在事件处理完成后稍后进行处理。在事件结束时,所有更改都一起刷新,您看不到中间状态。

使用批处理时,当执行最终的set-state事件时,无法回忆起prevState已更新(异步),这就是为什么最终状态似乎只反映了上一个事件所做的更改的原因。

将回调函数用作setStudy方法的第一个参数将有助于您返回中间状态并避免出现批处理问题。更新器功能将循环遍历所有合并的状态,并将其与先前的组件状态进行浅合并。这样可以确保我们在最终到达最终更新之前,接收所有状态更新并使用所有中间状态。

  const setValue = (section, key, value) => {
    setStudy(study => {
      return {
        ...study,
        [section]: { ...study[section], [key]: value }
      };
    });
  };

另请参见工作沙箱:https://codesandbox.io/s/delicate-tree-vqrz7