使用记忆时反应子组件不同步
React child components out of sync when using memoization
我有一个由几个“更大”子组件组成的组件,其中一个是自定义表单 CustomForm
,它又由几个自定义输入字段组成 CustomField
.
为了提高页面的性能,我想记住该表单,以便它仅在至少 data
值之一发生变化时才重新呈现(data
是主要组件的状态).
同时,我还想记住各个输入字段,因此它们只会在各自的值发生变化时重新呈现。
以下是一个最小的工作示例 - 我省略了应用程序的其他部分并简化了表单和输入字段:
import React, { useState} from 'react';
function CustomFieldComponent(props) {
const { labelText, data, handleChange } = props;
console.log("renders", labelText);
return (
<input id={labelText} key={labelText} value={data[labelText]} onChange={handleChange}/>
)
}
function compareTextfield(prevProps, nextProps) {
return prevProps.data[prevProps.labelText] === nextProps.data[nextProps.labelText];
}
const CustomField = React.memo(CustomFieldComponent, compareTextfield);
function CustomFormComponent(props) {
const { data, handleChange } = props;
return (
<div>
<CustomField labelText="field1" data={data} handleChange={handleChange}/>
<CustomField labelText="field2" data={data} handleChange={handleChange}/>
<CustomField labelText="field3" data={data} handleChange={handleChange}/>
</div>
)
}
function compareForm(prevProps, nextProps) {
return prevProps.data === nextProps.data;
}
const CustomForm = React.memo(CustomFormComponent, compareForm);
export default function Test(props) {
const [data, setData] = useState({"field1": "value1", "field2": "value2", "field3": "value3"});
const handleChange = (e) => {
var local_data = {...data};
local_data[e.target.id] = e.target.value;
setData(local_data);
}
// other parts of the component
return (
<div>
<CustomForm data={data} handleChange={handleChange}/>
<br/>
This is data:
{
Object.keys(data).map((key, index) => (
<div key={index}>{data[key]}</div>
))
}
</div>
)
}
关于不同组件的不必要的重新呈现,此代码按预期工作,但是各个输入字段不同步。例如,当我在 field1
中输入一些文本,然后在 field2
中输入一些文本时,field1
的先前输入被重置(即,先前在 data
中更改的值在随后的 data
中被覆盖输入)。
似乎问题出在 CustomFieldComponent
的记忆(使用 CustomField
),因为如果我在 [=22= 中使用非记忆的 CustomFieldComponent
本身], 一切都按预期工作,并且用户输入正确地保存在不同的字段中。
我的问题是,如何在保持各个输入字段与 data
同步的同时实现所需的记忆?
已找到修复方法。问题是主要组件中的 handleChange
函数。
必须是:
const handleChange = (e) => {
setData(prev => ({...prev, [e.target.id]: e.target.value}));
}
我有一个由几个“更大”子组件组成的组件,其中一个是自定义表单 CustomForm
,它又由几个自定义输入字段组成 CustomField
.
为了提高页面的性能,我想记住该表单,以便它仅在至少 data
值之一发生变化时才重新呈现(data
是主要组件的状态).
同时,我还想记住各个输入字段,因此它们只会在各自的值发生变化时重新呈现。
以下是一个最小的工作示例 - 我省略了应用程序的其他部分并简化了表单和输入字段:
import React, { useState} from 'react';
function CustomFieldComponent(props) {
const { labelText, data, handleChange } = props;
console.log("renders", labelText);
return (
<input id={labelText} key={labelText} value={data[labelText]} onChange={handleChange}/>
)
}
function compareTextfield(prevProps, nextProps) {
return prevProps.data[prevProps.labelText] === nextProps.data[nextProps.labelText];
}
const CustomField = React.memo(CustomFieldComponent, compareTextfield);
function CustomFormComponent(props) {
const { data, handleChange } = props;
return (
<div>
<CustomField labelText="field1" data={data} handleChange={handleChange}/>
<CustomField labelText="field2" data={data} handleChange={handleChange}/>
<CustomField labelText="field3" data={data} handleChange={handleChange}/>
</div>
)
}
function compareForm(prevProps, nextProps) {
return prevProps.data === nextProps.data;
}
const CustomForm = React.memo(CustomFormComponent, compareForm);
export default function Test(props) {
const [data, setData] = useState({"field1": "value1", "field2": "value2", "field3": "value3"});
const handleChange = (e) => {
var local_data = {...data};
local_data[e.target.id] = e.target.value;
setData(local_data);
}
// other parts of the component
return (
<div>
<CustomForm data={data} handleChange={handleChange}/>
<br/>
This is data:
{
Object.keys(data).map((key, index) => (
<div key={index}>{data[key]}</div>
))
}
</div>
)
}
关于不同组件的不必要的重新呈现,此代码按预期工作,但是各个输入字段不同步。例如,当我在 field1
中输入一些文本,然后在 field2
中输入一些文本时,field1
的先前输入被重置(即,先前在 data
中更改的值在随后的 data
中被覆盖输入)。
似乎问题出在 CustomFieldComponent
的记忆(使用 CustomField
),因为如果我在 [=22= 中使用非记忆的 CustomFieldComponent
本身], 一切都按预期工作,并且用户输入正确地保存在不同的字段中。
我的问题是,如何在保持各个输入字段与 data
同步的同时实现所需的记忆?
已找到修复方法。问题是主要组件中的 handleChange
函数。
必须是:
const handleChange = (e) => {
setData(prev => ({...prev, [e.target.id]: e.target.value}));
}