反应备忘录组件并在传递函数作为道具时重新渲染

时间:2019-01-25 15:59:42

标签: javascript reactjs memo

假设我有以下React组件:

const Compo1 = ({theName}) => {
  return (
    <Nested foo={() => console.log('Dr. ' + theName)}/>
  );
};

const Compo2 = ({theName}) => {
  function theFoo() {
    console.log('Dr. ' + theName);
  }
  return (
    <Nested foo={theFoo}/>
  );
};

还有嵌套在memo中的嵌套组件:

const Nested = React.memo(({foo}) => {
  return (
    <Button onClick={foo}>Click me</Button>
  );
});

foo中传递的函数在Compo1中是always recreated,也是Compo2,对吗?

如果是这样,由于foo每次都会收到一个新函数,这是否意味着memo将无用,因此Nested将始终被重新渲染?

3 个答案:

答案 0 :(得分:1)

memo函数将比较每个不同的道具,包括函数。但是,通过在每个渲染器的组件内部重新定义功能,您每次都会创建一个不同的引用,从而触发重新渲染。

尽管,如您在Comp3中所见,您仍然可以使用备忘,并通过声明外部函数来避免重新渲染:

class App extends React.Component {
    constructor(props) {
        super(props)

        this.state = {

        }
    }

    componentDidMount = () => {
        setInterval(() => { this.setState({ e: true }) }, 2000)
    }

    render() {
        return (
            <div>
                <Compo1 />
                <Compo2 />
                <Compo3 />
            </div>
        )
    }
}

const Compo1 = () => <Nested foo={() => console.log('Comp1 rendering')} />

const Compo2 = () => {
    function theFoo() {
        console.log('Comp2 rendering');
    }
    return <Nested foo={theFoo} />
};

const foo3 = function (text) { console.log('Comp3 rendering ' + text) }

const Compo3 = () => <Nested foo={foo3} />

const Nested = React.memo(({ foo }) => {
    foo('a param')
    return <div />
})


ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'>

答案 1 :(得分:1)

您可以使用新的钩子Api(React> = 16.8)来避免重新创建回调函数。

只需使用useCallback挂钩即可。

例如

父组件

import React, { useCallback} from 'react';

const ParentComponent = ({theName}) => {
  const theFoo = () => {
    console.log('Dr. ' + theName);
  }

  const memoizedCallback = useCallback(theFoo , []);

  return (
     <Nested foo={theFoo}/>
   );
};

useCallback将返回回调的记忆版本,只有在其中一个依赖项已更改(在第二个参数中传递)时,该回调的备注版本才会更改 在这种情况下,我们将空数组作为依赖项传递,因此该函数将仅创建一次。

和嵌套组件:

import React, { memo } from 'react';

const Nested = ({foo}) => (
  <Button onClick={foo}>Click me</Button>
);

export default memo(Nested);

有关更多信息-https://reactjs.org/docs/hooks-reference.html#usecallback

答案 2 :(得分:0)

我发现最好的方法是使用useRef。具体来说,我将此结构与Formik一起使用,以防止重新呈现一长串输入值。

const MyMemoizedGroupList = React.memo(
  ({
    groups,
    arrayHelpersRef,
  }) => {
    // [..] stuff
  }
}

function MainComponent() {
  const groupsArrayHelpersRef = React.useRef();

  return (
    <Formik 
      // [..] stuff
      render={({ values }) => (
        <Form>
          <FieldArray
            name="groups"
            render={arrayHelpers => {
              groupsArrayHelpersRef.current = arrayHelpers;
            }}
          />
          <MyMemoizedGroupList 
            groups={values.groups} 
            arrayHelpersRef={groupsArrayHelpersRef} 
          /> 
          {/* [..] stuff */}
        </Form>
      )} 
    />
  );
}