将 React PureComponent 重构为基于钩子的功能组件

Refactoring a React PureComponent to a hooks based functional component

我有一个工作 class based implementation of an Accordion component which I'm trying to refactor to use the new hooks api

我的主要挑战是找到一种方法来仅重新渲染切换的 <AccordionSection />,同时防止所有其他 <AccordionSection/> 组件在每次父级状态 [=13] 时重新渲染=](跟踪其状态的打开部分)已更新。

在基于 class 的实现中,我设法通过将 <AccordionSection /> 设为 PureComponent,传递 isOpenonClick 来实现这一点通过使用 context API、 的高阶组件回调它,方法是将这些回调保存在父 <Accordion/> 组件的状态中如下:

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

据我所知,它保留了对它们的引用,从而防止它们在每次 <Accordion /> 更新时被创建为新实例。

但是,我似乎无法让它与基于挂钩的实现一起使用。

有些我已经尝试过但没有成功的事情:

  1. memo 包装 Accordion 部分 - 在第二个回调参数上包括各种渲染条件。

  2. useCallback 包装 onClickisOpen 回调(似乎不起作用,因为它们有依赖项,每个 <Accordion/> 都会更新渲染)

  3. onClickisOpen 保存到这样的状态:const [callbacks] = useState({onClick, isOpen}) 然后将 callbacks 对象作为 ContextProvider value。 (似乎是错误的,并没有奏效)

以下是对我基于 class 的工作实现的引用:

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

我的钩子重构尝试:

https://codesandbox.io/s/lxp8xz80z7

我将日志保存在 <AccordionSection/> 渲染上,以展示我试图阻止的重新渲染。

任何输入将不胜感激。

所以我在追了太多兔子之后最后添加了这个小金块..

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.

useCallback 包装他 onClick 的创作可以解决这个问题。

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

虽然我似乎确实在这个过程中遇到了问题 exclusive 因为它现在总是启用的..

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

哦,我确实在那里移动了其他一些可能有助于修复的部分..

更新

重构为使用 useReducer 并将所有逻辑移至那里,以便我们可以提供稳定的 onClick

更新

他们说睡眠很好,但对我来说只是想入睡..

我知道我遗漏了什么.. 昨晚意识到我们不需要减速器,只需要 setState 的函数形式,它允许我们从内部访问最新状态useCallback 记忆功能。在此处转换了@itaydafna 的优化 https://codesandbox.io/s/8490v55029