import React, { useState, memo } from "react";
const Children = ({ name, value }: { name: string; value: number }) => {
// 每一次子组件更新时,都会触发
console.log("Children", "render");
return (
<div>
{name}-Children:{value}
</div>
);
};
// 模拟大型组件
const BigChildren = ({ handleMultiply }: { handleMultiply: any }) => {
// 使用for循环 执行代码阻塞0.5s
for (let i = 0; i < 100000000; i++) {
for (let j = 0; j < 5; j++) {}
}
console.log("BigChildren render");
return <div onClick={handleMultiply}>Multiply</div>;
};
// 每一次更新都比较前后的 props 是否相同,如果返回 true 就不渲染,返回 false 才渲染。
const MemoChildren = memo(Children);
const MemoBigChildren = memo(BigChildren);
const Counter = () => {
// 使用 useState 来管理计数状态
const [count, setCount] = useState(1);
// 每一次页面更新时,都会触发
console.log("count:", count, "render");
const handleAdd = () => {
// 使用 useState 更新状态,并触发重新渲染
setCount((prevCount) => prevCount + 1);
};
const handleMultiply = () => {
// 使用 useState 更新状态,并触发重新渲染
setCount((prevCount) => prevCount * 2);
};
const memoHandleMultiply = React.useCallback(handleMultiply, []);
return (
<div>
<div style={{ display: "flex", gap: "6px" }}>
<p>Count (useState): {count}</p>
<button type="button" onClick={handleAdd}>
add
</button>
</div>
{/* 这里你可以尝试切换4种情况,看一下memo的优化效果 */}
{/* <BigChildren handleMultiply={handleMultiply}></BigChildren> */}
{/* <BigChildren handleMultiply={memoHandleMultiply}></BigChildren> */}
{/* <MemoBigChildren handleMultiply={handleMultiply}></MemoBigChildren> */}
<MemoBigChildren handleMultiply={memoHandleMultiply}></MemoBigChildren>
<MemoChildren name="useState" value={count}></MemoChildren>
</div>
);
};
export default Counter;
- 在BigChildren组件中,通过一个双层循环模拟了一个耗时操作,这代表了在实际应用中可能遇到的大型组件或复杂计算,这些操作可能会导致组件渲染变慢。
- 使用memo优化:如上篇文章中讲到,memo是React提供的一个高阶组件,用于阻止组件的无谓渲染。通过将Children和BigChildren组件用memo包裹,可以实现仅在组件的props发生变化时才重新渲染的优化效果。
- 使用useCallback避免不必要的渲染:在React中,每次组件渲染都会创建新的函数实例,如果这些函数作为props传递给子组件,即使这些函数的实际作用并未改变,子组件也会因为接收到新的props而导致重新渲染。useCallback钩子用于缓存函数,以便跨渲染保持函数实例的一致性。在上述代码中,memoHandleMultiply使用了useCallback来缓存handleMultiply函数,这样即使Counter组件重新渲染,memoHandleMultiply也不会改变,从而避免了MemoBigChildren因接收到新的handleMultiply函数实例而重新渲染。
- 代码通过注释提供了四种情况的切换选项,以展示使用memo和useCallback前后性能的对比。当不使用这些优化技术时,即使是不必要的,BigChildren组件也会在每次Counter组件状态更新时重新渲染。而使用了这些技术后,只有在相关props发生变化时,MemoBigChildren才会重新渲染,从而提高了应用的性能。
在实际情况中,我们常常有一个大型组件,它的重渲染比较浪费时间,但是他的props是一些函数,或者通过一些 state 或者父组件的 props 计算出来的值。那么这个时候我们就需要对这些值进行一个缓存,当依赖项不变时,这些值或者函数他的地址就不会改变,那么 memo 进行比较的时候会相等这样就不会触发重新渲染了。