我有一个关于React的shouldComponentUpdate
的问题(当没有被覆盖时)。我更喜欢纯粹的功能组件,但我担心它每次都会更新,即使道具/状态没有改变。
所以我正在考虑使用PureComponent类。
我的问题是:
功能组件与PureComponents具有相同的shouldComponentUpdate
检查吗?或者它每次更新?
答案 0 :(得分:9)
在React中,功能组件是无状态的,它们没有生命周期方法。 无状态组件是编写React组件的一种优雅方式,在我们的包中没有太多代码。但在内部,无状态组件被包装在一个类中,而当前没有任何优化。这意味着无状态和有状态组件在内部具有相同的代码路径(尽管我们以不同方式定义它们)。
但是在未来,React可以优化无状态组件,如下所述:
将来,我们还可以进行性能优化 通过避免不必要的检查和记忆来特定于这些组件 分配。 [ More reading... ]
shouldComponentUpdate
这是我们可以应用自定义优化并避免不必要的重新呈现组件的地方。下面介绍了使用不同类型组件的方法:
功能无状态组件
如前所述,无状态组件没有生命周期方法,因此我们无法使用shouldComponentUpdate
对它们进行优化。但它们已经以不同的方式进行了优化,它们具有更简单和优雅的代码结构,并且比具有所有生命周期钩子的组件花费更少的字节。
扩展React.PureComponent
从 React v15.3.0 ,我们有一个名为 PureComponent
的新基类
通过内置的 PureRenderMixin
进行扩展。在引擎盖下,它使用当前道具/状态与shouldComponentUpdate
内的下一个道具/状态的浅层比较。
尽管如此,我们仍然不能依赖PureComponent
类来将我们的组件优化到我们想要的水平。如果我们拥有Object
类型的道具(数组,日期,普通对象),就会出现这种异常情况。这是因为我们在比较对象时遇到了这个问题:
const obj1 = { id: 1 };
const obj2 = { id: 1 };
console.log(obj1 === obj2); // prints false
因此,浅层比较不足以确定事情是否发生了变化。但如果你的道具只是字符串,数字,布尔值而不是对象,请使用PureComponent
类。如果您不想实现自己的自定义优化,也可以使用它。
扩展React.Component
考虑上面的例子;如果我们知道id
已更改后对象已更改,那么我们可以通过比较obj1.id === obj2.id
来实现我们自己的自定义优化。我们可以extend
我们的普通Component
基类,并使用shouldComponentUpdate
自行比较特定键。
答案 1 :(得分:5)
功能组件每次父母渲染时都会重新渲染,无论道具是否更改。
但是,使用React.memo
高阶组件实际上可以使功能组件获得与shouldComponentUpdate
https://reactjs.org/docs/react-api.html#reactmemo
PureComponent
检查
您只需在导出时将功能组件包装在React.memo
中,如此处所示。
所以
const SomeComponent = (props) => (<div>HI</div>)
export default SomeComponent
可以代替
const SomeComponent = (props) => (<div>HI</div>)
export default React.memo(SomeComponent)
示例
以下示例显示了这如何影响转播
父组件只是常规功能组件。它正在使用新的react hooks处理某些状态更新。
它只有一个tick
状态,仅用于提供一些线索来提示我们重新渲染道具的频率,而它每秒强制两次渲染父组件。
此外,我们还有一个clicks
状态,该状态表明我们单击该按钮的频率。这就是我们送给孩子们的道具。 因此,如果我们使用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);
查看示例
答案 2 :(得分:0)
另一种方法是仅在观看值更新时使用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]);