【问题标题】:React memo components and re-render when passing function as props将函数作为道具传递时反应备忘录组件并重新渲染
【发布时间】:2019-01-25 15:59:42
【问题描述】:

假设我有这些 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 中传递的函数是always recreatedCompo1Compo2,对吗?

如果是这样,既然foo每次都会收到一个新的函数,是不是意味着memo将无用,因此Nested总是会被重新渲染?

【问题讨论】:

    标签: javascript reactjs memo


    【解决方案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={memoizedCallback}/>
       );
    };
    

    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

    【讨论】:

    • 在我看来你应该在依赖数组中传递theName。否则,如果 theName 在您的父组件中发生更改,onClick 侦听器仍将具有旧值
    【解决方案2】:

    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'>

    【讨论】:

    • 如果你必须向这个外部函数传递参数怎么办?
    • 如果参数是从收到的 props 派生的呢?我更新了我的问题以反映这种情况。
    • 您将无法直接更改函数的行为方式。不过,您可以在调用函数时传递您想要的内容。
    【解决方案3】:

    我发现最好的方法是使用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>
          )} 
        />
      );
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-08
      • 2021-10-10
      • 2021-01-19
      • 2017-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多