[英]React functional component rerenders despite use of React.memo()
我對 React 還很陌生,即使我使用的是React.memo
高階組件,我也無法准確理解為什么會重新渲染不變的組件。
我有一個包含許多行元素的側邊欄。 行包含在其他組件中使用的數據; 所有組件共享行的“選擇”狀態。 在側邊欄中,我更改樣式以顯示每個元素的選擇 state。
一切都按預期運行,但隨着列表變長,性能擴展很差。 我認為部分原因是 React 重新渲染了側邊欄列表中的每個行元素,包括選擇 state 的元素沒有改變。 我想我可以通過使用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;
該組件是從代表整個側邊欄列表的父級調用的。 為了盡量減少不必要的 function 調用的數量(並且非常清楚在各個行中沒有發生任何副作用),我為每一行構建了一個元組列表,其中包含其 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;
它本身是從管理所有 state 的祖父母 class 調用的:
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
組件條目被重新渲染,我能做些什么來修復它?
祖父母中的 onClicked function 可能會在每次渲染時重新創建,因此您的行組件也會重新渲染。
解決方案是在祖父母中使用 React.useCallback。
const handleClicked = React.useCallback(() => {
...
}, [a, b])
其中 a 和 b 是依賴項,如果更改將需要重新渲染。
反應 useCallback文檔
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.