рефакторинг React PureComponent в функциональный компонент на основе хуков - PullRequest
5 голосов
/ 18 марта 2019

У меня есть работающая классовая реализация компонента Accordion , которую я пытаюсь реорганизовать , чтобы использовать новые хуки api .

Моя основная задача состоит в том, чтобы найти способ перерисовки только переключенного <AccordionSection />, не позволяя всем остальным <AccordionSection/> компонентам перерисовывать каждый раз состояние родительского <Accordion/> (которое отслеживает открытые разделы о его состоянии) обновляется.

В реализации на основе классов мне удалось добиться этого, сделав <AccordionSection /> a PureComponent, передав ему обратные вызовы isOpen и onClick через компонент более высокого порядка, который использует API context , и , сохраняя эти обратные вызовы в состоянии родительского компонента <Accordion/> следующим образом:

this.state = {
      /.../
      onClick: this.onClick,
      isOpen: this.isOpen
    };

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

Однако я не могу заставить это работать с реализацией на основе хуков.

Некоторые вещи, которые я уже пытался безуспешно:

  1. Обертывание раздела Accordion с memo - включая различные условия рендеринга для второго аргумента обратного вызова.

  2. упаковка обратных вызовов onClick и isOpen с useCallback (похоже, не работает, поскольку они имеют зависимости, которые обновляются при каждом <Accordion/> рендеринге)

  3. сохранение onClick и isOpen в такое состояние: const [callbacks] = useState({onClick, isOpen}) и последующую передачу объекта callbacks в качестве ContextProvider value. (кажется не так, и не работает)

Вот ссылки на мою реализацию на основе рабочего класса:

https://codesandbox.io/s/4pyqoxoz9

и попытка рефакторинга моих хуков:

https://codesandbox.io/s/lxp8xz80z7

Я сохранил логи на визуализации <AccordionSection/>, чтобы продемонстрировать, какие повторные рендеры я пытаюсь предотвратить.

Любые входные данные будут очень благодарны.

1 Ответ

2 голосов
/ 18 марта 2019

так что я закончил тем, что добавил этот маленький самородок после того, как преследовал слишком много кроликов ..

const cache = {};

const AccordionSection = memo(({ children, sectionSlug, onClick, isOpen }) => {
  if (cache[sectionSlug]) {
    console.log({
      children: children === cache[sectionSlug].children,
      sectionSlug: sectionSlug === cache[sectionSlug].sectionSlug,
      onClick: onClick === cache[sectionSlug].onClick,
      isOpen: isOpen === cache[sectionSlug].isOpen
    });
  }
  cache[sectionSlug] = { children, sectionSlug, onClick, isOpen };

Это показало, что менялось onClick.Что тогда кажется очевидным, так как компонент Accordion выполняет рендеринг и создание нового onClick.

, оборачивая его onClick созданием с useCallback, исправляя проблему.

const onClick = useCallback(
  sectionSlug =>
    setOpenSections({
      ...(exclusive ? {} : openSections),
      [sectionSlug]: !openSections[sectionSlug]
    }),
  []
);

хотя я делаюпохоже, что он сломал exclusive в процессе, поскольку он теперь всегда включен ..

https://codesandbox.io/s/1o08p08m27

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

Обновление

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

Обновление

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

Я знал, что чего-то не хватает ... понял вчера вечероммне не нужен редуктор, просто форма функции setState, которая позволяет нам получать доступ к актуальному состоянию из функции useCallback memoed.Конвертированная здесь оптимизация @ itaydafna https://codesandbox.io/s/8490v55029

...