React Hooks使用避坑指南
<p> 函数组件比类组件更加方便实现业务逻辑代码的分离和组件的复用,函数组件也比类组件轻量,没有react hooks之前,函数组件是无法实现LocalState的,这导致有localstate状态的组件无法用函数组件来书写,这限制了函数组件的应用范围,而react hooks扩展了函数组件的能力。可是在使用的过程中,也要注意下面这些问题,否则就会掉进坑里,造成性能损失。按照下面的方法做,,才能避开这些陷阱。</p><h3>1. 将与状态改变无关的变量和方法提取到组件函数外面</h3>
<p>每次状态改变时,整个函数组件都会重新执行一遍。导致函数组件内部定义的方法和变量,都会重新创建,重新给它们分配内存,这会导致性能受到影响。</p>
<div class="cnblogs_code">
<pre>import React, {useState,useCallback} from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 测试每次状态改变时,方法是不是重新分配内存</span>
let testFooMemoAlloc = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Set();
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'每次状态改变,函数组件从头开始执行'<span style="color: rgba(0, 0, 0, 1)">)
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const calc </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>+ 1<span style="color: rgba(0, 0, 0, 1)">);
}
const bar </span>=<span style="color: rgba(0, 0, 0, 1)"> {
a:</span>1<span style="color: rgba(0, 0, 0, 1)">,
b:</span>2<span style="color: rgba(0, 0, 0, 1)">,
c: </span>'与状态无关的变量定义'<span style="color: rgba(0, 0, 0, 1)">
}
const doFoo </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'与状态无关的方法'<span style="color: rgba(0, 0, 0, 1)">);
}
testFooMemoAlloc.add(doFoo)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={calc}>加1</button>
<p>count:{count}</p>
<p>testFooMemoAlloc.size增加的话,说明每次都重新分配了内存:{testFooMemoAlloc.size}</p>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210620114429360-297559282.png"></p>
<p>与改变状态相关的变量和方法,必须放在hooks组件内,而无状态无关的变量和方法,可以提取到函数组件外,避免每次状态更新,都重新分配内存。也可以分别使用useMemo和useCallback包裹变量与函数,也能达到同样的效果,后面会讲。</p>
<div class="cnblogs_code">
<pre>import React, {useState,useCallback} from "react"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 测试每次状态改变时,方法是不是重新分配内存</span>
let testFooMemoAlloc = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Set();
const bar </span>=<span style="color: rgba(0, 0, 0, 1)"> {
a:</span>1<span style="color: rgba(0, 0, 0, 1)">,
b:</span>2<span style="color: rgba(0, 0, 0, 1)">,
c: </span>'与状态无关的变量定义'<span style="color: rgba(0, 0, 0, 1)">
}
const doFoo </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'与状态无关的方法'<span style="color: rgba(0, 0, 0, 1)">);
}
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'每次状态改变,函数组件从头开始执行'<span style="color: rgba(0, 0, 0, 1)">)
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const calc </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>+ 1<span style="color: rgba(0, 0, 0, 1)">);
}
testFooMemoAlloc.add(doFoo)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={calc}>加1</button>
<p>count:{count}</p>
<p>testFooMemoAlloc.size增加的话,说明每次都重新分配了内存:{testFooMemoAlloc.size}</p>
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210620114730720-906731731.png"></p>
<h3>2. 用memo对子组件进行包装</h3>
<p>父组件引入子组件,会造成一些不必要的重复渲染,每次父组件更新count,子组件都会更新。</p>
<div class="cnblogs_code">
<pre>import React,{useState} from "react"<span style="color: rgba(0, 0, 0, 1)">;
const Child </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'子组件?'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><div>我是一个子组件</div>
<span style="color: rgba(0, 0, 0, 1)"> );
}
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<Child />
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619113115717-428773525.png"></p>
<p>使用memo,count变化子组件没有更新</p>
<div class="cnblogs_code">
<pre>import React,{useState,memo} from "react"<span style="color: rgba(0, 0, 0, 1)">;
const Child </span>= memo((props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'子组件?'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><div>我是一个子组件</div>
<span style="color: rgba(0, 0, 0, 1)"> );
})
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<Child />
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619112918014-570271533.png"></p>
<h3>给memo传入第二个参数,开启对象深度比较。当子组件传递的属性值未发生改变时,子组件不会做无意义的render。</h3>
<p>memo不仅适用于函数组件,也适用于class组件,是一个高阶组件,默认情况下只会对复杂对象做浅层比较,如果想做深度比较,可以传入第二个参数。与 <code>shouldComponentUpdate</code> 不同的是,deepCompare 返回 <code>true</code> 时,不会触发 render,如果返回 <code>false</code>,则会。而 <code>shouldComponentUpdate</code> 刚好与其相反。</p>
<div class="cnblogs_code">
<pre>import React, {useState, memo } from "react"<span style="color: rgba(0, 0, 0, 1)">;
import deepCompare from </span>"./deepCompare"<span style="color: rgba(0, 0, 0, 1)">;
const Child </span>= memo((props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<div>我是一个子组件</div>
<div>{ props.fooObj.a}</div>
</>
<span style="color: rgba(0, 0, 0, 1)"> );
}, deepCompare)
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const </span>= useState({ a: 1, b: { c: 2<span style="color: rgba(0, 0, 0, 1)"> } })
console.log(</span>'页面开始渲染'<span style="color: rgba(0, 0, 0, 1)">)
const calc </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
setCount(count </span>+ 1<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (count === 3<span style="color: rgba(0, 0, 0, 1)">) {
setFooObj({ b: { c: </span>2<span style="color: rgba(0, 0, 0, 1)"> }, a: count })
}
}
const doBar </span>= () =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'给子组件传递方法,测试一下是否会引起不必须的渲染'<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={calc}>加1</button>
<p>count:{count}</p>
<Child fooObj={fooObj} doBar={doBar} />
</>
<span style="color: rgba(0, 0, 0, 1)"> )
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 深度比较两个对象是否相等</span>
export <span style="color: rgba(0, 0, 255, 1)">default</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> deepCompare(prevProps: any, nextProps: any) {
const len: number </span>=<span style="color: rgba(0, 0, 0, 1)"> arguments.length;
let leftChain: any[] </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
let rightChain: any </span>=<span style="color: rgba(0, 0, 0, 1)"> [];
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> // console.log({ arguments });</span>
<span style="color: rgba(0, 128, 0, 1)">//
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (len < 2<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> console.log('需要传入2个对象,才能进行两个对象的属性对比');</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> for (let i = 1; i < len; i++) {</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> leftChain = [];</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> rightChain = [];</span>
<span style="color: rgba(0, 0, 0, 1)">console.log({ prevProps, nextProps });
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">compare2Objects(prevProps, nextProps, leftChain, rightChain)) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> console.log('两个对象不相等');</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> }</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> console.log('两个对象相等');</span>
<span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> compare2Objects(prevProps: any, nextProps: any, leftChain: any, rightChain: any) {
</span><span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> p;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 两个值都为为NaN时,在js中是不相等的, 而在这里认为相等才是合理的</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (isNaN(prevProps) && isNaN(nextProps) && <span style="color: rgba(0, 0, 255, 1)">typeof</span> prevProps === 'number' && <span style="color: rgba(0, 0, 255, 1)">typeof</span> nextProps === 'number'<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 原始值比较</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps ===<span style="color: rgba(0, 0, 0, 1)"> nextProps) {
console.log(</span>'原始值'<span style="color: rgba(0, 0, 0, 1)">, prevProps, nextProps);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造类型比较</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (
(</span><span style="color: rgba(0, 0, 255, 1)">typeof</span> prevProps === 'function' && <span style="color: rgba(0, 0, 255, 1)">typeof</span> nextProps === 'function') ||<span style="color: rgba(0, 0, 0, 1)">
(prevProps </span><span style="color: rgba(0, 0, 255, 1)">instanceof</span> Date && nextProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Date) ||<span style="color: rgba(0, 0, 0, 1)">
(prevProps </span><span style="color: rgba(0, 0, 255, 1)">instanceof</span> RegExp && nextProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span> RegExp) ||<span style="color: rgba(0, 0, 0, 1)">
(prevProps </span><span style="color: rgba(0, 0, 255, 1)">instanceof</span> String && nextProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span> String) ||<span style="color: rgba(0, 0, 0, 1)">
(prevProps </span><span style="color: rgba(0, 0, 255, 1)">instanceof</span> Number && nextProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Number)
) {
console.log(</span>'function', prevProps.toString() ===<span style="color: rgba(0, 0, 0, 1)"> nextProps.toString());
</span><span style="color: rgba(0, 0, 255, 1)">return</span> prevProps.toString() ===<span style="color: rgba(0, 0, 0, 1)"> nextProps.toString();
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 两个比较变量的值如果是null和undefined,在这里会退出</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (!(prevProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span> Object && nextProps <span style="color: rgba(0, 0, 255, 1)">instanceof</span><span style="color: rgba(0, 0, 0, 1)"> Object)) {
console.log(prevProps, nextProps, </span>'prevProps instanceof Object && nextProps instanceof Object'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps.isPrototypeOf(nextProps) ||<span style="color: rgba(0, 0, 0, 1)"> nextProps.isPrototypeOf(prevProps)) {
console.log(</span>'prevProps.isPrototypeOf(nextProps) || nextProps.isPrototypeOf(prevProps)'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造器不相等则两个对象不相等</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps.constructor !==<span style="color: rgba(0, 0, 0, 1)"> nextProps.constructor) {
console.log(</span>'prevProps.constructor !== nextProps.constructor'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 原型不相等则两个对象不相等</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps.prototype !==<span style="color: rgba(0, 0, 0, 1)"> nextProps.prototype) {
console.log(</span>'prevProps.prototype !== nextProps.prototype'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (leftChain.indexOf(prevProps) > -1 || rightChain.indexOf(nextProps) > -1<span style="color: rgba(0, 0, 0, 1)">) {
console.log(</span>'leftChain.indexOf(prevProps) > -1 || rightChain.indexOf(nextProps) > -1'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 遍历下次的属性对象,优先比较不相等的情形</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> (p <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> nextProps) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (nextProps.hasOwnProperty(p) !==<span style="color: rgba(0, 0, 0, 1)"> prevProps.hasOwnProperty(p)) {
console.log(</span>'nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
} </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> nextProps !== <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> prevProps) {
console.log(</span>'typeof nextProps !== typeof prevProps'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> console.log('p in prevProps');</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 遍历上次的属性对象,优先比较不相等的情形</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> (p <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> prevProps) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否都存在某个属性值</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (nextProps.hasOwnProperty(p) !==<span style="color: rgba(0, 0, 0, 1)"> prevProps.hasOwnProperty(p)) {
console.log(</span>'nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 属性值的类型是否相等</span>
<span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> nextProps !== <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> prevProps) {
console.log(</span>'typeof nextProps !== typeof prevProps'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
console.log(</span>'typeof prevProps', <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> prevProps);
</span><span style="color: rgba(0, 0, 255, 1)">switch</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)"> prevProps) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对象类型和函数类型的处理</span>
<span style="color: rgba(0, 0, 255, 1)">case</span> 'object'<span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 0, 255, 1)">case</span> 'function'<span style="color: rgba(0, 0, 0, 1)">:
leftChain.push(prevProps);
rightChain.push(nextProps);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">compare2Objects(prevProps, nextProps, leftChain, rightChain)) {
console.log(</span>'!compare2Objects(prevProps, nextProps, leftChain, rightChain)'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
leftChain.pop();
rightChain.pop();
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">default</span><span style="color: rgba(0, 0, 0, 1)">:
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 基础类型的处理</span>
<span style="color: rgba(0, 0, 255, 1)">if</span> (prevProps !==<span style="color: rgba(0, 0, 0, 1)"> nextProps) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210620174927908-1248372267.png"></p>
<h3>3.用useCallback对组件方法进行包装</h3>
<p>当父组件传递方法给子组件的时候,memo好像没什么效果,无论是用const定义的方法,还在用箭头函数或者bind定义的方法,子组件还是执行了</p>
<div class="cnblogs_code">
<pre>import React, { useState,memo } from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件会有不必要渲染的例子</span>
<span style="color: rgba(0, 0, 0, 1)">interface ChildProps {
changeName: ()</span>=><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}
const FunChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'普通函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是普通函数子组件</div>
<button onClick={changeName}>普通函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const FunMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(FunChild);
const ArrowChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'箭头函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是箭头函数子组件</div>
<button onClick={changeName.bind(<span style="color: rgba(0, 0, 255, 1)">null</span>,'test')}>箭头函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const ArrowMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(ArrowChild);
const BindChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'Bind函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是Bind函数子组件</div>
<button onClick={changeName}>Bind函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const BindMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(BindChild);
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const name </span>= "test"<span style="color: rgba(0, 0, 0, 1)">;
const changeName </span>= <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() {
console.log(</span>'测试给子组件传递方法,使用useCallback后,子组件是否还会进行无效渲染'<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ArrowMemochangeName={()=>changeName()}/>
<BindMemochangeName={changeName.bind(<span style="color: rgba(0, 0, 255, 1)">null</span>)}/>
<FunMemo changeName={changeName} />
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p> <img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619172621461-130370346.png"></p>
<p>使用useCallback,参数为[],页面初始渲染后,改变count的值,传递普通函数的子组件不再渲染, 传递箭头函数和bind方式书写的方法的子组件还是会渲染</p>
<div class="cnblogs_code">
<pre>import React, { useState,memo ,useCallback} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件会有不必要渲染的例子</span>
<span style="color: rgba(0, 0, 0, 1)">interface ChildProps {
changeName: ()</span>=><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}
const FunChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'普通函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是普通函数子组件</div>
<button onClick={changeName}>普通函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const FunMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(FunChild);
const ArrowChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'箭头函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是箭头函数子组件</div>
<button onClick={changeName.bind(<span style="color: rgba(0, 0, 255, 1)">null</span>,'test')}>箭头函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const ArrowMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(ArrowChild);
const BindChild </span>= ({ changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'Bind函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div>我是Bind函数子组件</div>
<button onClick={changeName}>Bind函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const BindMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(BindChild);
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const name </span>= "test"<span style="color: rgba(0, 0, 0, 1)">;
const changeName </span>= useCallback(() =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'测试给子组件传递方法,使用useCallback后,子组件是否还会进行无效渲染'<span style="color: rgba(0, 0, 0, 1)">);
},[])
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<ArrowMemochangeName={()=>changeName()}/>
<BindMemochangeName={changeName.bind(<span style="color: rgba(0, 0, 255, 1)">null</span>)}/>
<FunMemo changeName={changeName} />
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619172039979-1080864587.png"></p>
<p> 另外这里要注意一点,如果useCallback包裹的方法用到了状态变量,要把依赖的状态变量传进去,否则拿不到最新的状态值</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">const</span> foo= useCallback(<span style="color: rgba(0, 0, 255, 1)">async</span> () =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">const</span> { bar } = <span style="color: rgba(0, 0, 0, 1)">fooState;
</span><span style="color: rgba(0, 0, 255, 1)">const</span> res = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> ajax({
api: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">api.bar</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 0, 255, 1)">params</span><span style="color: rgba(0, 0, 0, 1)">: { bar },
isLoading: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">,
});
</span><span style="color: rgba(0, 0, 255, 1)">return</span> res?.retdata?<span style="color: rgba(0, 0, 0, 1)">.ifSucc;<br>
}, );</span></pre>
</div>
<h3>4.用useMemo对组件中的对象变量进行包装</h3>
<p>在子组件使用了memo,useCallback的情况下,给子组件传递一个对象属性,对象值和方法都未发生改变的情况下,父组件无关状态变更,子组件也会重新渲染。</p>
<div class="cnblogs_code">
<pre>import React, { useState,memo ,useCallback} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件会有不必要渲染的例子-使用了memo,useCallback的情况下,给子组件传递一个对象属性值</span>
<span style="color: rgba(0, 0, 0, 1)">interface ChildProps {
childStyle: { color: string; fontSize: string;};
changeName: ()</span>=><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}
const FunChild </span>= ({ childStyle,changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'普通函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div style={childStyle}>我是普通函数子组件</div>
<button onClick={changeName}>普通函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const FunMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(FunChild);
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const childStyle </span>= {color:'green',fontSize:'16px'<span style="color: rgba(0, 0, 0, 1)">};
const changeName </span>= useCallback(() =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'测试给子组件传递方法,使用useCallback后,子组件是否还会进行无效渲染'<span style="color: rgba(0, 0, 0, 1)">);
},[])
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<FunMemo childStyle={childStyle} changeName={changeName} />
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p> <img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619154407431-249926134.png"></p>
<p> </p>
<p> 使用useMemo可以解决给子组件传递对象属性时的不必要更新问题。</p>
<div class="cnblogs_code">
<pre>import React, { useState,memo, useMemo, useCallback} from 'react'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">子组件会有不必要渲染的例子</span>
<span style="color: rgba(0, 0, 0, 1)">interface ChildProps {
childStyle: { color: string; fontSize: string;};
changeName: ()</span>=><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}
const FunChild </span>= ({ childStyle,changeName}: ChildProps): JSX.Element =><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span>'普通函数子组件'<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">(
</span><>
<div style={childStyle}>我是普通函数子组件</div>
<button onClick={changeName}>普通函数子组件按钮</button>
</>
<span style="color: rgba(0, 0, 0, 1)">);
}
const FunMemo </span>=<span style="color: rgba(0, 0, 0, 1)"> memo(FunChild);
const Page </span>= (props:any) =><span style="color: rgba(0, 0, 0, 1)"> {
const </span>= useState(0<span style="color: rgba(0, 0, 0, 1)">);
const </span>= useState(""<span style="color: rgba(0, 0, 0, 1)">);
const childStyle </span>= {color:'green',fontSize:'16px'<span style="color: rgba(0, 0, 0, 1)">};
const changeName </span>= useCallback(() =><span style="color: rgba(0, 0, 0, 1)"> {
setName(</span>'变一下名称'<span style="color: rgba(0, 0, 0, 1)">)
}, [])
const childStyleMemo </span>= useMemo(() =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> {
color: name </span>=== '变一下名称' ? 'red':'green'<span style="color: rgba(0, 0, 0, 1)">,
fontSize: </span>'16px'<span style="color: rgba(0, 0, 0, 1)">
}
}, )
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><>
<button onClick={(e) => { setCount(count+1) }}>加1</button>
<p>count:{count}</p>
<FunMemo childStyle={childStyleMemo} changeName={changeName} />
</>
<span style="color: rgba(0, 0, 0, 1)">)
}
export </span><span style="color: rgba(0, 0, 255, 1)">default</span> Page;</pre>
</div>
<p><img src="https://img2020.cnblogs.com/blog/861554/202106/861554-20210619155652729-618059055.png"></p>
<p> </p><br><br>
来源:https://www.cnblogs.com/wangpenghui522/p/14902574.html
頁:
[1]