Реагировать на компоненты памятки и выполнять рендеринг при передаче функции в качестве реквизита - PullRequest
0 голосов
/ 25 января 2019

Предположим, у меня есть следующие компоненты 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 is всегда воссоздан в Compo1, а также Compo2, верно?

Если так, то, поскольку foo каждый раз получает новую функцию, означает ли это, что memo будетбесполезно, поэтому Nested всегда будет перерисован?

Ответы [ 3 ]

0 голосов
/ 30 мая 2019

Вы можете использовать новые перехватчики 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

0 голосов
/ 19 июня 2019

Самый хороший способ, который я нашел, это использовать 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>
      )} 
    />
  );
}
0 голосов
/ 25 января 2019

Функция 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'>
...