我对React还是很陌生,即使我正在使用React.memo
高阶组件,我也很难准确地理解为什么不变的组件会被重新渲染。
我有一个侧边栏,其中包含许多行元素。行包含其他组件中使用的数据;所有组件共享行的“选择”状态。在边栏中,我更改了样式以显示每个元素的选择状态。
一切正常,但是随着列表的增加,性能伸缩性很差。我认为部分原因是由于React重新渲染了侧边栏列表中的每个行元素,包括那些选择状态未更改的元素。我以为可以使用React.memo
来阻止这种重新渲染,但是似乎没有什么作用。
这是每个列表条目的代码:
import React from 'react';
// The only props that might change value are the labels string and
// the styles rowStyle and labelStyle, which caller populates
// with 'selected' or 'unselected' styles based on row state
const Row = React.memo(({
rowId, labels = "", rowStyle = {}, labelStyle = {},
onClicked // callback from grandparent, which updates selections (w/ modifier keys)
}) => {
console.log(`Rendering row ${rowId}`) // to report when rows rerender
return (
<div
key={rowId}
style={rowStyle}
onClick={(event) => onClicked(rowId, event)}
>
<span>{rowId}</span>
<span style={labelStyle}>{ labels }</span>
</div>
);
})
export default Row;
从代表整个侧边栏列表的父级调用此组件。为了最大程度地减少不必要的函数调用次数(并明确说明在各行中没有发生任何副作用),我为具有ID,样式,标签和标签的每行建立一个元组列表-风格。
列表的内容被传递到Row
组件,并且在大多数情况下应该在两次调用之间是相同的(因此触发备忘录和避免重新提交),但是不似乎是。
import React from 'react';
import Row from '../pluginComponents/Row';
import Styles from './common/Styles'; // to ensure the references aren't changing
// onClicked is passed in from the parent component and handles changing the selections
const ListDiv = React.memo(({ rowIds, onClicked, rowLabels, styling, selections }) => {
const tuples = rowIds.reduce((priors, rowId) => {
return {
...priors,
[rowId]: {
'style': Styles.unselectedStyle,
'labelStyle': Styles.unselectedLabelStyle,
'labels': ((rowLabels[rowId] || {}).labels || []).join(", ")
}
}
}, {});
Object.keys(selections).forEach((rowId) => {
if (!tuples[rowId]) return;
tuples[rowId]['style'] = Styles.selectedStyle;
tuples[rowId]['labelStyle'] = Styles.selectedLabelStyle;
});
return (
<div style={styling}>
{rowIds.map((rowId) => (
<Row
key={rowId}
rowId={rowId}
labels={tuples[rowId]['labels']}
rowStyle={tuples[rowId]['style']}
labelStyle={tuples[rowId]['labelStyle']}
onClicked={onClicked}
/>
))}
</div>
)
})
const RowList = ({ list, selections = {}, onClicked, labels={}, styling }) => {
if (!list) return (<div>Empty list</div>);
return (
<div>
<ListDiv
rowIds={list}
onClicked={onClicked}
rowLabels={labels}
styling={styling}
selections={selections}
/>
</div>
);
}
export default RowList;
本身是从管理所有状态的祖父母类中调用的:
const Grandparent = (props) => {
...
return (
...
<div>
{
(status !== 'complete') ? (
<div><CircularProgress /></div>
) : (
<RowList list={data.list}
selections={selections} // tracked with useState
onClicked={handleClicked} // calls some functions defined in this component
labels={data.labels || {}}
styling={foo}
/>
)
}
...
);
...
为什么我应该记住的Row
组件条目会被重新呈现,我该怎么做才能解决?
答案 0 :(得分:1)
可以在每个渲染器上重新创建Grandparent中的onClicked函数,因此也可以重新渲染行组件。
解决方案是在祖父母中使用React.useCallback。
const handleClicked = React.useCallback(() => {
...
}, [a, b])
a和b是依赖项,如果更改将需要重新呈现。
反应useCallback docs