React - 用作导致额外渲染的props

IT技术 javascript reactjs
2021-05-03 16:32:57

我正在处理一些繁重的表格。因此,我试图在我能找到的任何地方挤压性能。最近我添加了Why-did-you-render插件,以更深入地了解可能会减慢我的页面速度的原因。我注意到,例如,当我单击有关所有其他组件的复选框组件时会重新渲染。理由总是一样的。WDYR 说

由于 props 变化而重新渲染:具有相同名称的不同函数 {prev onChangeHandler: ƒ} "!==" {next onChangeHandler: ƒ}

我尽可能地尊重我发现的最佳实践指示。我的组件传递的回调函数遵循这种模式

import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
export function TopLevelComponent({props}){

    const defaultData = {name: '', useMale: false, useFemale: false}

    const [data, setData] = useState(defData);
    const { t } = useTranslation();
    const updateState = (_attr, _val) => {
        const update = {};
        update[_attr] = _val;
        setData({ ...data, ...update });
    }

    const updateName = (_v) => updateState('name', _v);//Text input
    const updateUseMale = (_v) => updateState('useMale', _v);//checkbox
    const updateUseFemale = (_v) => updateState('useFemale', _v);//checkbox
    ...

    return <div>
        ...
        <SomeInputComponent value={data.name} text={t('fullName')} onChangeHandler={updateName} />
        <SomeCheckboxComponent value={data.useMale} onChangeHandler={updateUseMale} text={t('useMale')}/>
        <SomeCheckboxComponent value={data.useFemale} onChangeHandler={updateUseFemale} text={t('useFemale')}/>
        ...
    </div>
}

在这样的示例中,更改任何输入(例如:在文本输入中写入文本或单击其中一个复选框)将导致其他 2 个组件以上述理由重新呈现。

我想我可以停止使用功能组件并利用该shouldComponentUpdate()功能,但功能组件确实提供了一些我宁愿保留的优势。我应该如何编写我的函数,以便与一个输入交互不会强制更新另一个输入?

3个回答

问题源于您定义更改处理程序的方式:

const updateName = (_v) => updateState('name', _v)

每次渲染都会调用这一行,因此,每次渲染组件时,props都有一个新的(尽管功能方面相同)值。这同样适用于所有其他处理程序。

作为一个简单的解决方案,您可以将功能组件升级为完全成熟的组件并在渲染函数之外缓存处理程序,也可以shouldComponentUpdate()在子组件中实现。

您需要memo为您的子组件使用以减少渲染

const SomeInputComponent  = props => {

};

export default memo(SomeInputComponent);

// if it still causes rerender witout any prop change then you can use callback to allow or block render

e.f.

function arePropsEqual(prevProps, nextProps) {
  return prevProps.name === nextProps.name; // use your logic to determine if props are same or not
}

export default memo(SomeInputComponent, arePropsEqual);

/* One reason for re-render is that `onChange` callback passed to child components is new on each parent render which causes child components to re-render even if you use `momo` because function is updated on each render so in order to fix this, you can use React hook `useCallback` to get the same function reference on each render.


So in you parent component, you need to do something like 
*/

import { useCallback } from 'react';


const updateName = useCallback((_v) => updateState('name', _v), [])

您必须在传递给子级之前记住父函数,使用 useCallback 作为功能组件或转换为类属性(如果您使用类)。

export default class Parent extends React.PureComponent {
    constructor(props) {
       super(props);
       this.onClick = this.onClick.bind(this);
    }

    onClick() {
       console.log("click");
    }

    render() {
        return (
           <ChildComponent
               onClick={ this.onClick }
           />
        );
    }
}

使用回调:

Parent = () => {
   const onClick = useCallback(
      () => console.log('click'),
      [] 
   );

   return (
        <ChildComponent
            onClick={onClick}
        />
   );
}