React-用作道具,导致额外的渲染

时间:2019-05-17 02:07:54

标签: javascript reactjs

我正在处理一些繁重的表格。因此,我想尽可能地压缩性能。最近,我添加了“为什么要渲染”插件,以获取更多有关可能使我的页面变慢的信息​​。例如,当我单击有关所有其他组件的复选框组件时,我注意到了。理由始终是相同的。 WDYR说

  

由于道具变更而重新渲染:与道具不同的功能   同名{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 个答案:

答案 0 :(得分:1)

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

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

在每次渲染时都会调用此行,因此,每次渲染您的组件时,道具都有一个新的(尽管功能上相同)值。其他所有处理程序也是如此。

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

答案 1 :(得分:0)

您需要为子组件使用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), [])

答案 2 :(得分:0)

在传递给子代之前,必须先记住父函数,使用useCallback作为功能组件,或者如果使用class,则转换为class属性。

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 }
           />
        );
    }
}

with useCallback:

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

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