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

ita*_*fna 6 javascript performance components reactjs react-hooks

我有一个基于工作类的Accordion组件的实现,我正在尝试重构以使用新的hooks api

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

在基于类的实现中,我设法实现了这一点,方法是使<AccordionSection />a PureComponent,通过使用API 的高阶组件将isOpenand onClick回调传递给它context以及将这些回调保存在父<Accordion/>组件的状态下,如下所示:

this.state = {
      /.../
      onClick: this.onClick,
      isOpen: this.isOpen
    };
Run Code Online (Sandbox Code Playgroud)

据我所知,它保留了对它们的引用,因此阻止了在每次<Accordion />更新时将它们创建为新实例。

但是,我似乎无法将其与基于钩子的实现一起使用。

我已经尝试不成功的一些事情:

  1. memo- 包裹“手风琴”部分,包括第二个回调参数上的各种渲染条件。

  2. onClick和包装回车isOpenuseCallback(似乎不起作用,因为它们具有在每个<Accordion/>渲染器上更新的依赖项)

  3. onClick和保存isOpen到这样的状态:const [callbacks] = useState({onClick, isOpen})然后将callbacks对象作为传递ContextProvider value。(似乎错了,没用)

这是对基于工作类的实现的引用:

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

和我的钩子重构尝试:

https://codesandbox.io/s/lxp8xz80z7

我将日志保留在<AccordionSection/>渲染器上,以演示要阻止的重新渲染。

任何输入将不胜感激。

lec*_*tor 7

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

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 };
Run Code Online (Sandbox Code Playgroud)

这表明情况onClick正在发生变化。当 Accordion 组件正在渲染并创建一个新的onClick.

包装他的onClick创作useCallback纠正了这个问题。

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

虽然我似乎exclusive在这个过程中已经坏了,因为它现在总是启用..

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

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

更新

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

更新

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

我知道我遗漏了一些东西......昨晚意识到我们不需要减速器,只是它的函数形式setState允许我们从useCallbackmemoed 函数中访问最新状态。在这里转换@itaydafna 的优化https://codesandbox.io/s/8490v55029