所以我对钩子还不陌生。我想运行一些基本验证。遇到一个奇怪的问题:当我背对背运行两个钩子时,两个钩子中只有第二个起作用。
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()是异步的。我想知道这里是否正在发生类似的事情?
答案 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
}
}));