如果传递变量而不是显式文本,React setState 钩子不会更新依赖元素
React setState hook not updating dependent element if passed a variable as opposed to explicit text
我正处于折腾 React 和只使用 vanilla JS 的边缘,但我想我会先在这里检查一下。我只是想将包含对象的变量的内容传递到状态中,并让其更新依赖于它的元素。如果我向 setState
传递一个包含该对象的变量,它就不起作用。如果我将它传递的对象的显式文本传递给它。
使用 React v 18.0.0
function buttonHandler(e) {
e.preventDefault()
let tmpObject = {...chartData}
tmpObject.datasets[0].data = quoteData.map(entry => entry['3'])
tmpObject.datasets[1].data = quoteData.map(({fastEma}) => fastEma)
tmpObject.datasets[2].data = quoteData.map(({slowEma}) => slowEma)
tmpObject.labels = quoteData.map(entry => new Date(entry.timestamp).toLocaleTimeString())
console.log("from button:", tmpObject)
setChartData(prevState => {
console.log("tmpObject",tmpObject)
return tmpObject
})
return <div>
<button onClick={buttonHandler}>Update</button>
<Line options={chartOptions} data={chartData}/>
</div>
当我 运行 上面的内容时,console.log 的输出与它应该的完全一样,但元素不会更新。如果我从控制台复制对象输出并将其显式粘贴到代码中,它确实有效。
function buttonHandler(e) {
e.preventDefault()
setChartData({...})
我已经尝试了以下语句的所有可以想象的变体,但都无济于事...
return {...prevState, ...tmpObject}
如果有任何建议,我将不胜感激。
编辑:
作为另一个测试,我添加了以下 HTML 元素以查看它是否已更新。它得到更新并显示预期数据。尽管如此,我还是很难理解为什么图表会在我传递显式文本时更新,但如果我传递一个变量则不会更新。
<p>{`${new Date().toLocaleTimeString()} {JSON.stringify(chartData)}`</p>
问题是状态突变。即使您浅复制了 chartData
状态,您也应该记住这是引用复制。每个 属性 仍然是对原始 chartData
对象的引用。
function buttonHandler(e) {
e.preventDefault();
let tmpObject = { ...chartData }; // <-- shallow copy ok
tmpObject.datasets[0].data = quoteData.map(entry => entry['3']); // <-- mutation!!
tmpObject.datasets[1].data = quoteData.map(({ fastEma }) => fastEma); // <-- mutation!!
tmpObject.datasets[2].data = quoteData.map(({ slowEma }) => slowEma); // <-- mutation!!
tmpObject.labels = quoteData.map(
entry => new Date(entry.timestamp).toLocaleTimeString()
);
console.log("from button:", tmpObject);
setChartData(prevState => {
console.log("tmpObject",tmpObject);
return tmpObject;
});
}
在 React 中,不仅下一个状态需要是一个新的对象引用,任何正在更新的嵌套状态也是如此。
参见 Immutable Update Pattern - 这是一个 Redux 文档,但真正解释了为什么使用可变更新是 React 的关键。
function buttonHandler(e) {
e.preventDefault();
setChartData(chartData => {
const newChartData = {
...chartData, // <-- shallow copy previous state
labels: quoteData.map(
entry => new Date(entry.timestamp).toLocaleTimeString()
),
datasets: chartData.datasets.slice(), // <-- new datasets array
};
newChartData.datasets[0] = {
...newChartData.datasets[0], // <-- shallow copy
data: quoteData.map(entry => entry['3']), // <-- then update
};
newChartData.datasets[1] = {
...newChartData.datasets[1], // <-- shallow copy
data: quoteData.map(({ fastEma }) => fastEma), // <-- then update
};
newChartData.datasets[2] = {
newChartData.datasets[2], // <-- shallow copy
data: quoteData.map(({ slowEma }) => slowEma), // <-- then update
};
return newChartData;
});
}
使用依赖于 chartData
状态的 useEffect
挂钩检查您的工作:
useEffect(() => {
console.log({ chartData });
}, [chartData]);
如果仍然存在更新问题,请检查 Line
组件的代码,看看它是否正在对传递的 data
prop 进行任何类型的安装记忆。
我正处于折腾 React 和只使用 vanilla JS 的边缘,但我想我会先在这里检查一下。我只是想将包含对象的变量的内容传递到状态中,并让其更新依赖于它的元素。如果我向 setState
传递一个包含该对象的变量,它就不起作用。如果我将它传递的对象的显式文本传递给它。
使用 React v 18.0.0
function buttonHandler(e) {
e.preventDefault()
let tmpObject = {...chartData}
tmpObject.datasets[0].data = quoteData.map(entry => entry['3'])
tmpObject.datasets[1].data = quoteData.map(({fastEma}) => fastEma)
tmpObject.datasets[2].data = quoteData.map(({slowEma}) => slowEma)
tmpObject.labels = quoteData.map(entry => new Date(entry.timestamp).toLocaleTimeString())
console.log("from button:", tmpObject)
setChartData(prevState => {
console.log("tmpObject",tmpObject)
return tmpObject
})
return <div>
<button onClick={buttonHandler}>Update</button>
<Line options={chartOptions} data={chartData}/>
</div>
当我 运行 上面的内容时,console.log 的输出与它应该的完全一样,但元素不会更新。如果我从控制台复制对象输出并将其显式粘贴到代码中,它确实有效。
function buttonHandler(e) {
e.preventDefault()
setChartData({...})
我已经尝试了以下语句的所有可以想象的变体,但都无济于事...
return {...prevState, ...tmpObject}
如果有任何建议,我将不胜感激。
编辑: 作为另一个测试,我添加了以下 HTML 元素以查看它是否已更新。它得到更新并显示预期数据。尽管如此,我还是很难理解为什么图表会在我传递显式文本时更新,但如果我传递一个变量则不会更新。
<p>{`${new Date().toLocaleTimeString()} {JSON.stringify(chartData)}`</p>
问题是状态突变。即使您浅复制了 chartData
状态,您也应该记住这是引用复制。每个 属性 仍然是对原始 chartData
对象的引用。
function buttonHandler(e) {
e.preventDefault();
let tmpObject = { ...chartData }; // <-- shallow copy ok
tmpObject.datasets[0].data = quoteData.map(entry => entry['3']); // <-- mutation!!
tmpObject.datasets[1].data = quoteData.map(({ fastEma }) => fastEma); // <-- mutation!!
tmpObject.datasets[2].data = quoteData.map(({ slowEma }) => slowEma); // <-- mutation!!
tmpObject.labels = quoteData.map(
entry => new Date(entry.timestamp).toLocaleTimeString()
);
console.log("from button:", tmpObject);
setChartData(prevState => {
console.log("tmpObject",tmpObject);
return tmpObject;
});
}
在 React 中,不仅下一个状态需要是一个新的对象引用,任何正在更新的嵌套状态也是如此。
参见 Immutable Update Pattern - 这是一个 Redux 文档,但真正解释了为什么使用可变更新是 React 的关键。
function buttonHandler(e) {
e.preventDefault();
setChartData(chartData => {
const newChartData = {
...chartData, // <-- shallow copy previous state
labels: quoteData.map(
entry => new Date(entry.timestamp).toLocaleTimeString()
),
datasets: chartData.datasets.slice(), // <-- new datasets array
};
newChartData.datasets[0] = {
...newChartData.datasets[0], // <-- shallow copy
data: quoteData.map(entry => entry['3']), // <-- then update
};
newChartData.datasets[1] = {
...newChartData.datasets[1], // <-- shallow copy
data: quoteData.map(({ fastEma }) => fastEma), // <-- then update
};
newChartData.datasets[2] = {
newChartData.datasets[2], // <-- shallow copy
data: quoteData.map(({ slowEma }) => slowEma), // <-- then update
};
return newChartData;
});
}
使用依赖于 chartData
状态的 useEffect
挂钩检查您的工作:
useEffect(() => {
console.log({ chartData });
}, [chartData]);
如果仍然存在更新问题,请检查 Line
组件的代码,看看它是否正在对传递的 data
prop 进行任何类型的安装记忆。