Функциональный субкомпонент не рендерится при смене реквизита - PullRequest
1 голос
/ 16 марта 2020

У меня очень тяжелый (вычислительный) функциональный компонент (Parent), который не имеет состояния и имеет несколько дочерних подкомпонентов с локальным состоянием. Дети зависят только от реквизита, отправленного от родителей.

Я передаю функцию одному из потомков (ChildA), чтобы изменить значение переменной в Parent.

Эта переменная является одним из реквизитов другого дочернего компонента (ChildB), который имеет состояние на основе этого реквизита и обновляет его в хуке useEffect.

Компонент ChildB не выполняет повторную визуализацию когда значение, переданное как prop, изменяется в родительском компоненте.

Конечно, введение состояния (ловушка useState) в Parent исправляет это, но перерисовывает родительский элемент снова и снова и убивает производительность, поскольку Parent имеет 500+ вложенных компонентов, которые все повторно визуализируются.

Введение какого-либо магазина (Redux, MobX), вероятно, решит проблему, но это будет излишним.

Упрощенный пример:

import React, { useEffect, useState } from "react";

export default function App() {
  return <Parent />    
}

const ChildA = ({ onAction }) => {
  return <button onClick={onAction}>CLICK</button>;
};

const ChildB = ({ coreValue }) => {
  const [value, setValue] = useState(0);

  useEffect(() => {
    setValue(coreValue);
  }, [coreValue]);

  return <div>My value: {value}</div>;
};

const Parent = () => {
  let calculatedValue = 0;

  const changeValue = () => {
    calculatedValue += Math.random();
  };

  return (
    <div>
      <ChildA onAction={changeValue} />
      <ChildB coreValue={calculatedValue} />
    </div>
  );
};

Вы можете проверить код здесь: https://codesandbox.io/s/vigilant-wave-r27rg

Как мне перерисовать только ChildB при смене реквизита?

Ответы [ 2 ]

0 голосов
/ 16 марта 2020

React's useCallback и memo предотвращают ненужный повторный рендеринг. Обратите внимание, что ChildA не выполняет повторную визуализацию независимо от количества изменений состояния Parent или ChildB. Кроме того, ваш текущий пример не нуждается в useState / useEffect в ChildB

https://codesandbox.io/s/usecallback-and-memo-ptkuj

import React, { useEffect, useState, memo, useCallback } from "react";

export default function App() {
  return <Parent />;
}

const ChildA = memo(({ onAction }) => {
  console.log("ChildA rendering");
  return <button onClick={onAction}>CLICK</button>;
});

const ChildB = memo(({ coreValue }) => {
  const [value, setValue] = useState(0);

  useEffect(() => {
    setValue(coreValue);
  }, [coreValue]);

  return <div>My value: {value}</div>;
});

const Parent = () => {
  const [calculatedValue, setCalculatedValue] = useState(0);

  const changeValue = useCallback(() => {
    setCalculatedValue(c => (c += Math.random()));
  }, []);

  return (
    <div>
      <ChildA onAction={changeValue} />
      <ChildB coreValue={calculatedValue} />
    </div>
  );
};
0 голосов
/ 16 марта 2020

Вы должны сохранить значение в состоянии родительского компонента и просто отправить значение состояния родительского компонента в ChildB, там вам не нужно поддерживать состояние и ловушку useEffect, чтобы поймать изменение. Смотрите код здесь: https://codesandbox.io/s/adoring-chatelet-sjjfs

import React, { useState } from "react";
export default function App() {
  return <Parent />;
}
const ChildA = ({ onAction }) => {
  return <button onClick={onAction}>CLICK</button>;
};
const ChildB = ({ coreValue }) => {
  return <div>My value: {coreValue}</div>;
};
const Parent = () => {
  const [value, setValue] = useState(0);
  const changeValue = () => {
    setValue(value + Math.random());
  };
  return (
    <div>
       <ChildA onAction={changeValue} />
       <ChildB coreValue={value} />
    </div>
  );
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...