17

假设我有这些 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而且Compo2,对吗?

如果是这样,既然foo每次都接收到一个新的函数,是不是就意味着memo没用了,所以Nested总是会重新渲染?

4

3 回答 3

16

您可以使用新的钩子 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

于 2019-05-30T09:53:00.683 回答
1

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

虽然,正如您在 中看到的Comp3,您仍然可以使用 memo 并通过声明外部函数来避免重新渲染:

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

于 2019-01-25T16:33:35.153 回答
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>
      )} 
    />
  );
}
于 2019-06-19T13:45:16.260 回答