【问题标题】:Create a Pure Wrapper for a React Component为 React 组件创建一个纯包装器
【发布时间】:2021-07-07 12:57:34
【问题描述】:

我正在使用 why-did-you-render 包来调试我的 react 应用程序中的一些性能问题。我收到以下控制台消息,说明我的 XDrag 组件为何重新渲染:

XDrag
defaultNotifier.js:40 {XDrag: ƒ} "Re-rendered because of props changes:"
defaultNotifier.js:45 props.children
defaultNotifier.js:49 different React elements (remember that the <jsx/> syntax always produces a *NEW* immutable React element so a component that receives <jsx/> as props always re-renders). (more info here)
defaultNotifier.js:52 {prev children: {…}} "!==" {next children: {…}}

the article linked to in this message, 中,他们建议在我们的原始组件周围使用“纯包装器”组件来避免这种重新渲染:

class PureFatherWrapper extends React.PureComponent{
  render(){
    return (
      <PureFather>
        <SomeChild/>
      </PureFather>
    )
  }
}
  1. 纯包装器/纯组件到底是什么意思?纯组件与其他种类的组件有何不同?
  2. 为什么这个纯包装器会避免重新渲染?
  3. 如何为我的 XDrag 组件实现 Pure Wrapper?
import React, { FC, ReactNode } from "react";
import { Draggable, DraggableProps } from "react-beautiful-dnd";

interface IXDrag extends Omit<DraggableProps, "children"> {
  className?: string;
  children: ReactNode;
  dragAll?: boolean;
}

const XDrag: FC<IXDrag> = ({ className, children, dragAll, ...props }) => {
  console.log(React.isValidElement(children));
  if (!React.isValidElement(children)) return <div />;
  return (
    <Draggable {...props}>
      {(provided, snapshot) => {
        const dragHandleProps = dragAll ? provided.dragHandleProps : {};
        return (
          <tr
            className={className}
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...dragHandleProps}
          >
            {React.cloneElement(children, { provided })}
          </tr>
        );
      }}
    </Draggable>
  );
};

使用 XDrag:

import React from "react";
import { useStoreState } from "../../hooks";
import XDrag from "../XDrag";
import TableHeader from "./TableHeader";

const ContentTable = () => {
  const cardItems = useStoreState(
    (state) => state.appModel.activeCards
  );

  return (
        <table>
          <tbody>
            <tr>
              <TableHeader
                title={"URL"}
              />
            </tr>
            {cardItems.map((card, i) => {
              return (
                <XDrag
                  draggableId={card.sourceId}
                  index={i}
                  key={i.toString()}
                  isDragDisabled={card.isActive}
                >
                    <td>{card.src}</td>
                </XDrag>
              );
            })}
          </tbody>
        </table>
  );
};

一些上下文: 我正在使用react beautiful dnd 库在表格中创建一些可拖动的行。每个XDrag 为所有&lt;td&gt;s 提供一个&lt;tr&gt;

【问题讨论】:

  • 您能否也发布一下您是如何使用&lt;XDrag&gt; 的?我的意思是,呈现它的父组件。

标签: reactjs


【解决方案1】:

我会尽力回答每个问题,并希望能给你一些额外的信息来帮助你做出决定。

1。纯包装器/纯组件到底是什么意思?纯组件与其他种类的组件有何不同?

简单来说,纯组件(或纯函数)是一个组件(或函数)对于相同的输入总是返回相同的输出(顺便说一下,这不是 React 独有的)。

示例:function add(x, y){return x + y} 是一个纯函数,因为 add(1, 2) 将始终返回 3,无论您运行多少次。

PureComponents 有同样的想法。您可以在official docs 中阅读有关PureComponents 的更多信息。

2。为什么这个纯包装器避免重新渲染

在您链接的文章中,这是回答这个问题的关键部分:

永远记住:&lt;jsx prop='a'/&gt; 语法只是React.createElement('jsx', {prop: 'a'}) 的糖。这意味着每当它的父亲重新渲染时,jsx 都会尝试使用新的 props 对象来更新自己。

PureComponent 会知道prop: a 没有改变,所以它不会重新渲染。发生这种情况是因为 Shallow Compare 实用程序。

3。如何为我的 XDrag 组件实现 Pure Wrapper?

您可以使用文章中的建议(创建一个 PureWrapper 组件并使用它来包装您的 &lt;XDrag&gt;)或使用 useCallback 挂钩(或 useMemo)。像这样的:

const ContentTable = () => {
  const cardItems = useStoreState((state) => state.appModel.activeCards);

  const renderCardItems = useCallback(() => {
    return cardItems.map((card, i) => {
      return (
        <XDrag draggableId={card.sourceId} index={i} key={i.toString()} isDragDisabled={card.isActive} >
          <td>{card.src}</td>
        </XDrag>
      );
    })
  }, [cardItems])
    
  return (
    <table>
      <tbody>
        <tr>
          <TableHeader title={"URL"} />
        </tr>
        {renderCardItems()}
      </tbody>
    </table>
  );
};

这里,useCallback 有一个依赖数组(如useEffect),这意味着这个函数只有在cardItems 改变时才会改变。换句话说,只要cardItems 相同,renderCardItems() 将始终返回相同的结果。


注意:

希望这有助于澄清您的一些问题。

也就是说,真正的问题是:“为什么需要优化这个重新渲染?”

我们经常在不需要时担心优化。 React 在这方面已经很不错了,今天的浏览器拥有更多的功能。除非您有特定要求或性能非常差,否则您应该不要为代码添加复杂性,以避免一些重新渲染。

事实上,“避免早期优化”是一种简洁的代码原则。

这是一篇非常好的文章,其中包含更多信息:https://kentcdodds.com/blog/usememo-and-usecallback

【讨论】:

  • 好答案。需要指出的一件事:useCallback 不起作用,但 useMemo 会。结帐this sandbox
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 2020-02-18
  • 2020-03-17
  • 1970-01-01
  • 2021-03-04
相关资源
最近更新 更多