React state hook更新后即使使用useEffect也无法正常存入数据库,存在延迟保存问题
问题根因
- React中
setState是异步操作,调用setDateEl、setValueEl、setUpdatedIncome后,当前渲染周期内拿到的state仍然是更新前的旧值,这是第一次操作无响应、第二次操作才保存上一次数据的核心原因。 - 直接修改
updatedIncome原数组的元素(updatedIncome[index] = incomeObject)属于直接变更state引用,可能导致React无法正确触发重渲染。 - 没必要通过
useEffect监听updatedIncome触发保存,完全可以在数据处理完成后直接调用保存逻辑,避免闭包捕获旧值的问题。
修复方案
首先删除监听updatedIncome的useEffect逻辑,按如下方式修改两个业务函数即可:
const handleIncome = () => { // 直接把表单值存在临时变量里,不依赖state的异步更新 const newDate = document.querySelector('#IncomeDate').value; const newValue = document.querySelector('#IncomeValue').value; let incomeObject = {}; incomeObject[ format(parse(newDate, 'yyyy-MM', new Date()), 'yyyy-MM-dd') ] = reverseFormatNumber(newValue); const index = updatedIncome .map((key) => Object.keys(key)[0]) .indexOf(Object.keys(incomeObject)[0]); let newUpdatedIncome; if (index !== -1) { // 先拷贝原数组再修改,不直接修改state的原数组 newUpdatedIncome = [...updatedIncome]; newUpdatedIncome[index] = incomeObject; } else { newUpdatedIncome = [...updatedIncome, incomeObject].sort( (a, b) => new Date(Object.keys(a)[0]) - new Date(Object.keys(b)[0]) ); } // 直接用处理好的新数据调用保存接口,不用等state更新 handleAddIncome({ incomes: newUpdatedIncome }, index); // 再更新本地state setDateEl(newDate); setValueEl(newValue); setUpdatedIncome(newUpdatedIncome); toggleAddIncome(null, true); };
const handleAddIncome = async (incomesObj, index) => { const config = { headers: { 'Content-Type': 'application/json', }, }; try { await axios.post(`/api/investments/${id}/incomes`, incomesObj, config); // 这里用传入的最新数据,不要用外层的旧updatedIncome避免闭包问题 const currentIncomes = incomesObj.incomes; const dates = currentIncomes .filter((invest) => Object.values(invest)[0] !== null) .map((key) => Object.keys(key).map((date) => date)) .flat() .map((data) => { let datePartes = data.split('-'); return `${datePartes[2]}/${datePartes[1]}/${datePartes[0]}`; }); const IncomesTemp = currentIncomes .filter((invest) => Object.values(invest)[0] !== null) .map((key) => Object.values(key)[0]); let temparray = []; for (let i = 0; i < IncomesTemp.length; i++) { temparray.push([dates[i], IncomesTemp[i]]); } setNewIncomes(temparray); setAccruedIncome( Number( currentIncomes .map((key) => Object.values(key)[0]) .reduce((acc, curr) => acc + curr, 0) .toFixed(2) ) ); } catch (err) { console.log(err.response?.data); } };
内容的提问来源于stack exchange,提问作者lucasbbs
相关产品推荐
相关产品推荐

