Как предотвратить повторный рендеринг дочернего компонента при использовании React-хуков и памятки? - PullRequest
0 голосов
/ 03 января 2019

Я только начал экспериментировать с перехватчиками React, и мне интересно, как я могу предотвратить повторный рендеринг дочернего компонента при повторном рендеринге родительского.Я ищу что-то похожее на возвращение false в componentDidUpdate.Кажется, моя проблема связана с обработчиком щелчков, который я вызываю в дочернем компоненте, чтобы изменить состояние родительского компонента.Поскольку функция создается в родительском компоненте, она создается по-новому на каждом родительском рендере, что вызывает изменение реквизита в дочернем компоненте, что затем приводит к повторному рендерингу дочернего элемента (я думаю) .Вот пример кода, который поможет проиллюстрировать ситуацию.

function Parent() {
    const [item, setItem] = useState({ name: "item", value: 0 });

    const handleChangeItem = () => {
        const newValue = item.value + 1;
        setItem({ ...item, value: newValue });
    };

    return <Child item={item} changeItem={handleChangeItem} />;
}

const Child = React.memo(function Child({ item, changeItem }) {
    function handleClick(){
        changeItem();
    }
    return (
        <div>
            Name: {item.name} Value: {item.value}
            <button onClick={handleClick}>change state in parent</button>
        </div>
    );
});

Как запретить рендеринг дочернего компонента при каждом рендеринге родительского компонента?Должен ли handleChangeItem в родительском объекте жить где-то еще, чтобы он не создавался заново при каждом рендере?Если так, как он получает доступ к item и setItem, возвращаемым useState?

Я довольно новичок, чтобы реагировать, и я только начал играть с хуками, поэтому я, вероятно, упускаю что-то очевидное.

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Шубхам Хатри точно ответил на первоначальный вопрос, но я добавляю этот ответ, чтобы указать на рекомендуемый способ избежать этой проблемы с обратным вызовом.

Из документов:

useReducer обычно предпочтительнее useState, если у вас есть сложные логика состояния, которая включает в себя несколько значений. Это также позволяет вам оптимизировать производительность для компонентов, которые вызывают глубокие обновления, потому что вы можете передать диспетчер вместо обратного вызова.

https://reactjs.org/docs/hooks-reference.html#usereducer

Из FAQ:

В больших деревьях компонентов мы рекомендуем использовать альтернативу функция отправки из useReducer через контекст ...

https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down

Ключом ко всему этому является то, что диспетчеризация никогда не меняется, в отличие от обратных вызовов, которые создаются при каждом рендеринге.

0 голосов
/ 04 января 2019

В вашем случае на самом деле не имеет смысла запоминать Child, потому что, если элемент изменяется, дочерний элемент должен повторно визуализироваться.Однако, если есть случай, когда реквизиты не изменяются, но дочерний объект выполняет повторный рендеринг из-за воссоздания функций, вы бы использовали хук useCallback для запоминания функций при каждом рендеринге.Кроме того, поскольку вы запомнили обработчик, вы должны использовать метод обратного вызова для обновления состояния, поскольку item внутри обработчика будет ссылаться только на значение, которое было у него при первоначальном создании функции

function Parent() {
  const [item, setItem] = useState({ name: "item", value: 0 });

  const handleChangeItem = useCallback(() => {
    setItem(prevItem => ({ ...prevItem, value: prevItem.value + 1 }));
  }, []);

  return (
    <>
      Name: {item.name} Value: {item.value}
      <Child changeItem={handleChangeItem} />
    </>
  );
}

const Child = React.memo(function Child({ item, changeItem }) {
  function handleClick() {
    changeItem();
  }
  console.log("child render");
  return (
    <div>
      <button onClick={handleClick}>change state in parent</button>
    </div>
  );
});

Рабочая демоверсия

PS Кредит @danAbramov за направление

...