簡體   English   中英

盡管使用了 React.memo(),但 React 功能組件仍會重新渲染

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM