React.memo 道具改变时不重新渲染
React.memo not re render if props change
我正在尝试使用 React 备忘录组件作为行从头开始创建一个 Table 组件,以防止不必要的重新渲染。
行单元格是一个 React 组件数组,由一个子函数创建,子函数以行数据、行 ID、行索引和一个 commitChange 函数作为参数。
commitChange 函数用于从一行设置 Table 状态。
这是代码:
<Table data={[{name: 1}, {name: 2}]}>
{({data, index, id, commitChange})=>
[
<div>Name: {data.name}</div>,
<TextBox value={data.name} onChange={(value)=>commitChange('name', parseInt(value, 10))}/>,
<TextBox value={data.place}/>
]
}
</Table>
TableComponents.jsx
export const Table = ({ data: initialData, maxRows = 10, children: makeChildren, primaryKey = 'ID' }) => {
const [TableData, setTableData] = useState(initialData);
console.log('table re-render')
useEffect(() => {
console.log('--->table data', TableData)
}, [TableData]);
useEffect(() => {
setTableData(initialData)
}, [initialData]);
const renderCells = useCallback((param) => {
return makeChildren(param)
}, [])
const commitChange = useCallback((field, value, index) => {
setTableData(prevTableData => {
const newState = Array.from(prevTableData)
newState[index][field] = value
return [...newState]
})
}, [])
const renderRows = () => {
return TableData.map((row, i) =>
<TableRow
key={row[primaryKey]}
index={i}
rowData={row}
cells={renderCells}
id={row[primaryKey]}
updateTableData={commitChange}
/>
)
}
//
return (
<table>
<tbody>
{
renderRows()
}
</tbody>
</table>
);
}
const TableRow = React.memo(({ index, rowData, cells, id, updateTableData }) => {
console.log('render row' + index, rowData)
useEffect(() => {
console.log('row ' + index + ' data', rowData)
}, [rowData])
const renderCells = () => {
return cells({ data: rowData, index, id, commitChange: (field, value) => updateTableData(field, value, index) }).map((c, i) => {
return (
<td key={i}>
{c}
</td>
)
})
}
return (
<tr>
{renderCells()}
</tr>
)
})
当一个元素被添加到 Table 组件的数据属性时,table 重新渲染并只渲染添加的行,这工作正常。
但是,当从第二个单元格的文本框中编辑行元素时,父 Table 组件上的数据已正确更新,但行不会重新呈现。
当我使用 React.memo areEqual 函数 (Documentation here) 检查传递给组件的 previus 和 next 属性时,数据属性是相同的。
table 组件在 Table 按行更新数据并执行 renderRows 函数但不重新呈现更新的行时重新呈现。
问题是什么?感谢帮助
P.S 不,我不希望外部库制作自定义 Table 元素
问题是您正在浅复制您的 TableData(使用 ...
),所以当您更新此处的值时:
newState[index][field] = value
...您正在改变数组中的项目,但该项目的引用 到 保持不变。一种解决方法是这样做:
setTableData(prevTableData =>
prevTableData.map((item, i) =>
i === index ? { ...item, [field]: value } : item,
),
)
现在更新字段时会创建一个新对象。
我正在尝试使用 React 备忘录组件作为行从头开始创建一个 Table 组件,以防止不必要的重新渲染。 行单元格是一个 React 组件数组,由一个子函数创建,子函数以行数据、行 ID、行索引和一个 commitChange 函数作为参数。 commitChange 函数用于从一行设置 Table 状态。 这是代码:
<Table data={[{name: 1}, {name: 2}]}>
{({data, index, id, commitChange})=>
[
<div>Name: {data.name}</div>,
<TextBox value={data.name} onChange={(value)=>commitChange('name', parseInt(value, 10))}/>,
<TextBox value={data.place}/>
]
}
</Table>
TableComponents.jsx
export const Table = ({ data: initialData, maxRows = 10, children: makeChildren, primaryKey = 'ID' }) => {
const [TableData, setTableData] = useState(initialData);
console.log('table re-render')
useEffect(() => {
console.log('--->table data', TableData)
}, [TableData]);
useEffect(() => {
setTableData(initialData)
}, [initialData]);
const renderCells = useCallback((param) => {
return makeChildren(param)
}, [])
const commitChange = useCallback((field, value, index) => {
setTableData(prevTableData => {
const newState = Array.from(prevTableData)
newState[index][field] = value
return [...newState]
})
}, [])
const renderRows = () => {
return TableData.map((row, i) =>
<TableRow
key={row[primaryKey]}
index={i}
rowData={row}
cells={renderCells}
id={row[primaryKey]}
updateTableData={commitChange}
/>
)
}
//
return (
<table>
<tbody>
{
renderRows()
}
</tbody>
</table>
);
}
const TableRow = React.memo(({ index, rowData, cells, id, updateTableData }) => {
console.log('render row' + index, rowData)
useEffect(() => {
console.log('row ' + index + ' data', rowData)
}, [rowData])
const renderCells = () => {
return cells({ data: rowData, index, id, commitChange: (field, value) => updateTableData(field, value, index) }).map((c, i) => {
return (
<td key={i}>
{c}
</td>
)
})
}
return (
<tr>
{renderCells()}
</tr>
)
})
当一个元素被添加到 Table 组件的数据属性时,table 重新渲染并只渲染添加的行,这工作正常。
但是,当从第二个单元格的文本框中编辑行元素时,父 Table 组件上的数据已正确更新,但行不会重新呈现。 当我使用 React.memo areEqual 函数 (Documentation here) 检查传递给组件的 previus 和 next 属性时,数据属性是相同的。 table 组件在 Table 按行更新数据并执行 renderRows 函数但不重新呈现更新的行时重新呈现。
问题是什么?感谢帮助
P.S 不,我不希望外部库制作自定义 Table 元素
问题是您正在浅复制您的 TableData(使用 ...
),所以当您更新此处的值时:
newState[index][field] = value
...您正在改变数组中的项目,但该项目的引用 到 保持不变。一种解决方法是这样做:
setTableData(prevTableData =>
prevTableData.map((item, i) =>
i === index ? { ...item, [field]: value } : item,
),
)
现在更新字段时会创建一个新对象。