【问题标题】:Refactoring a React PureComponent to a hooks based functional component将 React PureComponent 重构为基于钩子的功能组件
【发布时间】:2019-08-08 16:07:57
【问题描述】:

我有一个有效的class based implementation of an Accordion component,我是trying to refactor,可以使用新的hooks api

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

在基于类的实现中,我设法通过将<AccordionSection /> 设为PureComponent,通过使用@987654333 的高阶组件将isOpenonClick 回调传递给它来实现这一点@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 传递。 (好像错了,没用)

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

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

我的钩子重构尝试:

https://codesandbox.io/s/lxp8xz80z7

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

非常感谢任何意见。

【问题讨论】:

    标签: javascript reactjs performance components react-hooks


    【解决方案1】:

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

    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

    【讨论】:

    • 感谢@lecstor 抽出宝贵时间。但是,这并不能解决问题。我认为 Accordion 现在总是 exclusive 的原因是,用 useCallback 包装 onClick 而不将任何内容传递到(强制)依赖数组中可以防止回调中 openSections 的值发生变化,因此它只是解决了到提供给useState(一个空对象)的初始openSections。如果您像这样将[openSections] 传递到依赖数组中,则可以解决此问题:codesandbox.io/s/ox9z3x8ry9 但是 - 现在我们回到最初的多部分渲染问题:(
    • mm,是的。如果您将手风琴作为类组件,这很容易.. 或者,您可以useReducer 8) 我认为它现在可以工作了..
    • 很好的解决方案!通过使用useReducer(和onClick 中的dispatch),您取消了onClickopenSections 的依赖,这实际上使useCallback 包装它有效。感谢您的帮助:)
    • 这里是对您的解决方案的另一个小优化,它使用了一个更简单的减速器,它只跟踪 openSections 状态(“独占”实际上不必保留在状态上):codesandbox.io/s/1vq390no0j
    • 甜蜜!昨晚试图入睡时,我意识到我一直缺少什么..setState的函数形式..不需要减速器..codesandbox.io/s/8490v55029
    猜你喜欢
    • 2020-10-15
    • 2020-09-26
    • 2019-07-11
    • 2020-10-27
    • 2021-12-28
    • 2019-12-08
    • 2020-02-13
    • 2020-05-18
    • 2023-03-25
    相关资源
    最近更新 更多