反应挂钩-不一致更新

时间:2020-05-13 00:32:13

标签: reactjs react-hooks

所以我对钩子还不陌生。我想运行一些基本验证。遇到一个奇怪的问题:当我背对背运行两个钩子时,两个钩子中只有第二个起作用。

const [validationTracking, setValidationTracking] = useState({});

const setValidation = (idx, field, value) => {
  const validationCopy = cloneDeep(validationTracking);
  if (!validationCopy[idx]) {
    validationCopy[idx] = {};
  }
  validationCopy[idx][field] = value;

  setValidationTracking(validationCopy);
};

const validateInputs = () => {
  partnerInfo.forEach((object, idx) => {
    if (!object['title']) {
      setValidation(idx, 'title', true);
    }
    if (!object['body']) {
      setValidation(idx, 'body', true);
    }
  });
};

在上面的代码partnerInfo=[{title: '', body: ''}]

仅当我运行validateInputs

时,身体才会触发验证

如果数组具有多个项目,则只有最后一个字段的验证设置为true [{body: true}]

上面的输入应将validateTracking设置为[{title: true, body: true}],但似乎跳过或覆盖了较早的项目

我知道基于类的组件中的this.setState()是异步的。我想知道这里是否正在发生类似的事情?

2 个答案:

答案 0 :(得分:1)

使用useState钩子需要注意以下几点:

  • 在组件的下一次重新渲染(以及重新评估任何闭包)之前,状态更改将不会立即显示在组件逻辑中
  • 如果在单个渲染周期中多次调用状态挂钩的“设置者”,则只会收集最后一次状态更新,并在后续渲染周期中应用

第二点与您的问题更相关,因为代码中的forEach迭代多次调用状态挂钩的setValiadation设置方法。由于这些操作是在单个渲染周期中完成的,因此只有对setValiadation的最后一次调用才会产生明显的效果。

解决此问题的常用方法是将所有状态更改收集到单个对象中,并通过一次调用将它们应用到setter。您可以采用以下方法实现这一目标:

const [validationTracking, setValidationTracking] = useState({});

// Revised function
const updateValidation = (object, idx, field, value) => {
  const validationCopy = cloneDeep(object);
  if (!validationCopy[idx]) {
    validationCopy[idx] = {};
  }
  validationCopy[idx][field] = value;

  return validationCopy
};

const validateInputs = () => {

  // Call setter via a callback that transforms current state
  // into a new state object for the component
  setValidationTracking(state => {

    // Reduce partnerInfo array to a new state object
    return partnerInfo.reduce((acc, infoObject, idx) => {

      if (!infoObject['title']) {
        acc = updateValidation(acc, idx, 'title', true);
      }
      if (!infoObject['body']) {
        acc = updateValidation(acc, idx, 'body', true);
      }

      return acc;

    }, state);    

  });  
};

希望有帮助!

答案 1 :(得分:0)

您需要了解useState挂钩在功能上起作用。调用它时,它将触发组件的重新渲染,并将新的状态值传递给它。状态值是不可变的,它们不是对可以更改的值的引用。这就是为什么我们说React函数组件相对于它们的props充当纯函数。

因此,当您在一次更新期间两次调用setValidationTracking(validationCopy)时,您将发送两个状态更新,该状态更新是使用此迭代的当前状态计算的。

也就是说,当第二个循环调用cloneDeep(validationTracking)时,validationTracking并没有改变,因为没有发生由第一个循环触发的重新渲染,并且状态值是不可变的。

要解决此问题,您可以改为传递状态更新器功能:

setValidationTracking(currentValidationTracking => ({
  ...currentValidationTracking,
  [idx]: {
    ...(currentValidationTracking[idx] || {}),
    [field]: value
  }
}));