reactjs - 为 React 组件创建一个纯包装器
问题描述
我正在使用Why-did-you-render包来调试我的反应应用程序中的一些性能问题。我收到以下控制台消息,说明我的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: {…}}
在此消息链接到的文章中,他们建议在我们的原始组件周围使用“纯包装器”组件来避免这种重新渲染:
class PureFatherWrapper extends React.PureComponent{ render(){ return ( <PureFather> <SomeChild/> </PureFather> ) } }
- 纯包装器/纯组件到底是什么意思?纯组件与其他种类的组件有何不同?
- 为什么这个纯包装器避免重新渲染?
- 如何为我的 XDrag 组件实现纯包装器?
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库在表格中创建一些可拖动的行。每个为所有sXDrag
提供一个。<tr>
<td>
解决方案
我将尝试回答每个问题,并希望能给您一些额外的信息来帮助您做出决定。
1. 纯包装器/纯组件到底是什么意思?纯组件与其他种类的组件有何不同?
简单来说,纯组件(或纯函数)是一个组件(或函数),它总是为相同的输入返回相同的输出(顺便说一下,这不是 React 独有的)。
示例:function add(x, y){return x + y}
是一个纯函数,因为无论您运行多少次,它add(1, 2)
都会返回。3
PureComponents
有同样的想法。您可以PureComponents
在官方文档中了解更多信息。
2. 为什么这个纯包装器避免了重新渲染
在您链接的文章中,这是回答这个问题的关键部分:
永远记住:
<jsx prop='a'/>
语法只是一个糖:React.createElement('jsx', {prop: 'a'})
. 这意味着每当它的父亲重新渲染时,jsx 都会尝试使用新的 props 对象来更新自己。
APureComponent
会知道prop: a
没有改变,所以它不会重新渲染。发生这种情况是因为使用了Shallow Compare实用程序。
3. 如何为我的 XDrag 组件实现 Pure Wrapper?
您可以使用文章中的建议(创建一个 PureWrapper 组件并使用它来包装您<XDrag>
的. 像这样的东西:
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
改变。换句话说,renderCardItems()
只要cardItems
是相同的,就会总是返回相同的结果。
笔记:
希望这有助于澄清您的一些问题。
也就是说,真正的问题是:“为什么需要优化这个重新渲染?”
我们经常在不需要的时候担心优化。React 在这方面已经很不错了,今天的浏览器拥有更多的功能。除非您有特定要求或性能非常差,否则您不应该为了避免一些重新渲染而增加代码的复杂性。
事实上,“避免早期优化”是一种简洁的代码原则。
这是一篇非常好的文章,其中包含更多信息:https ://kentcdodds.com/blog/usememo-and-usecallback
推荐阅读
- html - 在 Google Chrome 中按 F12 时,如何以紧凑的形式打印 BeautifulSoup 对象?
- vue.js - 访问 Vue 数据时未定义的属性
- php - Laravel PHPUnit 返回 404
- swift - 如何在 SwiftUI 中构建水平图表
- ansible - 无法从具有“包含”的文件中访问变量
- python - 如何阻止 BeautifulSoup 将 HTML 实体解码为符号
- python - 带日期的 Python EWMA
- flutter - Flutter UI 列表项
- c++ - C ++异步函数调用无需等待和返回 - 如何?
- react-bootstrap - 使用 React 和 Bootstrap 创建导航栏