我有一个关于 React 的问题shouldComponentUpdate
(未覆盖时)。我确实更喜欢纯函数组件,但我担心它每次都会更新,即使 props/state 没有改变。所以我正在考虑改用 PureComponent 类。
我的问题是:函数组件shouldComponentUpdate
是否与 PureComponents具有相同的检查?还是每次都更新?
我有一个关于 React 的问题shouldComponentUpdate
(未覆盖时)。我确实更喜欢纯函数组件,但我担心它每次都会更新,即使 props/state 没有改变。所以我正在考虑改用 PureComponent 类。
我的问题是:函数组件shouldComponentUpdate
是否与 PureComponents具有相同的检查?还是每次都更新?
每次父级渲染时,功能组件都会重新渲染,无论props是否更改。
但是,使用React.memo
高阶组件实际上可以让功能组件获得与https://reactjs.org/docs/react-api.html#reactmemo 中shouldComponentUpdate
使用的相同的检查PureComponent
您可以简单地将您的功能组件包装React.memo
在导出中,如下所示。
所以
const SomeComponent = (props) => (<div>HI</div>)
export default SomeComponent
可以改为
const SomeComponent = (props) => (<div>HI</div>)
export default React.memo(SomeComponent)
例子
以下示例显示了这如何影响重新渲染
父组件只是一个普通的功能组件。它使用新的react 钩子来处理一些状态更新。
它只是有一些tick
状态,仅用于提供有关我们重新渲染 prop 的频率的一些线索的目的,同时它强制每秒重新渲染父组件两次。
此外,我们有一个clicks
状态,告诉我们点击按钮的频率。这就是我们送给孩子们的props。因此,如果我们使用React.memo
现在请注意,我们有两个不同类型的孩子。一个包裹着memo
一个没有。Child
未包装的,每次父重新渲染时都会重新渲染。MemoChild
被包装的,只会在 clicks 属性改变时重新渲染。
const Parent = ( props ) => {
// Ticks is just some state we update 2 times every second to force a parent rerender
const [ticks, setTicks] = React.useState(0);
setTimeout(() => setTicks(ticks + 1), 500);
// The ref allow us to pass down the updated tick without changing the prop (and forcing a rerender)
const tickRef = React.useRef();
tickRef.current = ticks;
// This is the prop children are interested in
const [clicks, setClicks] = React.useState(0);
return (
<div>
<h2>Parent Rendered at tick {tickRef.current} with clicks {clicks}.</h2>
<button
onClick={() => setClicks(clicks + 1)}>
Add extra click
</button>
<Child tickRef={tickRef} clicks={clicks}/>
<MemoChild tickRef={tickRef} clicks={clicks}/>
</div>
);
};
const Child = ({ tickRef, clicks }) => (
<p>Child Rendered at tick {tickRef.current} with clicks {clicks}.</p>
);
const MemoChild = React.memo(Child);
实时示例(也在CodePen 上):
在 React 中,函数式组件是无状态的,它们没有生命周期方法。无状态组件是编写 React 组件的一种优雅方式,我们的包中没有太多代码。但是在内部,无状态组件被包装在一个类中,当前没有应用任何优化。这意味着无状态和有状态组件在内部具有相同的代码路径(尽管我们对它们的定义不同)。
但是在未来 React 可能会优化无状态组件,正如这里所说的:
将来,我们还将能够通过避免不必要的检查和内存分配来针对这些组件进行性能优化。[更多阅读...]
shouldComponentUpdate
您可以在这里应用我们的自定义优化并避免不必要的组件重新渲染。下面解释了这种方法与不同类型组件的用法:
功能性无状态组件
如前所述,无状态组件没有生命周期方法,因此我们无法使用shouldComponentUpdate
. 但是它们已经以不同的方式进行了优化,它们具有更简单和优雅的代码结构,并且比具有所有生命周期钩子的组件花费更少的字节。
扩展 React.PureComponent
从React v15.3.0 开始,我们有一个新的基类调用PureComponent
来扩展PureRenderMixin
内置。在幕后,这采用了当前props/状态与shouldComponentUpdate
.
也就是说,我们仍然不能依靠PureComponent
类来将我们的组件优化到我们想要的水平。如果我们有Object
类型(数组、日期、普通对象)的props,就会发生这种异常情况。这是因为我们在比较对象时遇到了这个问题:
const obj1 = { id: 1 };
const obj2 = { id: 1 };
console.log(obj1 === obj2); // prints false
因此,肤浅的比较不足以确定事情是否发生了变化。但是PureComponent
如果你的 props 只是字符串、数字、布尔值......而不是对象,请使用类。如果您不想实现自己的自定义优化,也可以使用它。
扩展 React.Component
考虑上面的例子;如果我们知道对象已经改变了id
,那么我们可以通过比较来实现我们自己的自定义优化obj1.id === obj2.id
。这就是我们可以使用extend
我们的普通Component
基类并shouldComponentUpdate
用来自己进行特定键的比较的地方。
另一种方法是使用useMemo仅在更新监视值时更新值:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
在对象的情况下,在确保更新后,可以选择使用状态挂钩来缓存感兴趣变量的值。例如通过使用lodash
:
const [fooCached, setFooCached]: any = useState(null);
if (!_.isEqual(fooCached, foo)) {
setFooCached(foo);
}).
const showFoo = useMemo(() => {
return <div>Foo name: { foo.name }</div>
}, [fooCached]);