尽管使用React.memo(),React功能组件仍会重新渲染

时间:2020-08-18 21:38:17

标签: javascript reactjs

我对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组件条目会被重新呈现,我该怎么做才能解决?

1 个答案:

答案 0 :(得分:1)

可以在每个渲染器上重新创建Grandparent中的onClicked函数,因此也可以重新渲染行组件。

解决方案是在祖父母中使用React.useCallback。

const handleClicked = React.useCallback(() => {
    ...
}, [a, b])

a和b是依赖项,如果更改将需要重新呈现。

反应useCallback docs