react_hooks的useCallback,高阶函数memo
一、概念和作用
1、memo高阶函数:
memo解决的是函数式组件的无效渲染问题,当函数式组件重新渲染时,会先判断数据是否发生了变化。相当于类组件的PureComponent(默认提供ShouldComponentUpdate)
2、useCallback:
1)、useCallback会返回一个函数的memoized(记忆的)值
2)、在依赖不变的情况下,多次定义(如:函数)的时候,返回的值是相同的 。
3)、格式:
let 新的函数 = useCallback(曾经的函数, [依赖的值])
二、使用场景:
1、memo高阶函数的使用场景:
不论父组件是什么类型的组件,子组件是否渲染 :
1)、 如果子组件是类组件(继承自PureComponent)
? 那么是否渲染由props和state是否改变决定;
2)、如果子组件是函数式组件
? 只要父组件渲染,子组件会无条件渲染。如下是代码示例:
import { useState } from "react";
import SonFn from "./SonFn";
export default () => {
console.log("父组件");
const [count, setCount] = useState(1);
let changeCount = () => {
setCount(count + 1);
}
return (
<>
<h1>useCallback</h1>
<p>{count}</p>
<input type="button" value="修改count" onClick={changeCount} />
<hr />
<SonFn />
</>
)
}
./SonFn.js
export default ()=>{
console.log("子组件");
return (
<div>
<h5>子组件(函数式组件)</h5>
</div>
)
}
只要点击按钮"修改count”,父组件就会刷新,而子组件SonFn也会无条件渲染(这是无效的渲染)。
3)、解决方案:
? 把子组件用高阶函数memo进行包裹,就能解决子组件的无条件渲染问题,即:子组件的渲染就会由props和state决定,有点像类组件继承自PureComponent的感觉。
如下是代码(只需要把子组件的代码进行修改就行):
import React,{memo} from 'react'
const SonFn = ()=>{
console.log("子组件");
return (
<div>
<h5>子组件(函数式组件)</h5>
</div>
)
}
export default memo(SonFn);
2、useCallback的使用场景:
父组件是函数式组件,子组件也是函数式组件(并且用memo包裹)
1)、子组件的属性是数据:
? 如果数据不变化,那么子组件不渲染,如果数据发生变化,那么子组件渲染。这里就没有性能问题。
import { useState,useCallback } from "react";
import SonFn from "./SonFn";
export default () => {
console.log("父组件UseCallback");
const [count, setCount] = useState(1);
let changeCount = () => {
setCount(count + 1);
}
return (
<>
<h1>useCallback1</h1>
<p>{count}</p>
<input type="button" value="修改count" onClick={changeCount} />
<hr/>
{}
<SonFn count={count} />
</>
)
}
./SonFn.js
import React,{memo} from 'react'
const SonFn = ({count})=>{
console.log("子组件");
return (
<div>
<h5>子组件(函数式组件)</h5>
<p>{count}</p>
</div>
)
}
export default memo(SonFn);
2)、子组件的属性是函数时,就会出现问题:
? 父组件刷新了,子组件依然会刷新。因为,父组件(函数式)每次刷新时,函数都会重新定义,那么传给子组件的函数属性必然会发生变化。所以子组件会刷新,如下是示例代码:
import { useState } from "react";
import SonFn from "./SonFn";
export default () => {
console.log("父组件");
const [count, setCount] = useState(1);
let changeCount = () => {
setCount(count + 1);
}
let increment = ()=>{
console.log("increment");
}
return (
<>
<h1>useCallback</h1>
<p>{count}</p>
<input type="button" value="修改count" onClick={changeCount} />
<hr/>
<SonFn onMyClick={increment} />
</>
)
}
./SonFn.js
import React,{memo} from 'react'
const SonFn = ()=>{
console.log("子组件");
return (
<div>
<h5>子组件(函数式组件)</h5>
</div>
)
}
export default memo(SonFn);
3)、解决方案:把传给子组件的函数属性,用useCallback包裹。
格式:
let 新的函数 = useCallback(曾经的函数, [依赖的值])
如下是修改后的代码(只需要修改父组件的代码):
以下代码把increment函数进行了包裹
export default () => {
console.log("父组件");
const [count, setCount] = useState(1);
let changeCount = () => {
setCount(count + 1);
}
let increment = useCallback(()=>{
console.log("increment");
},[])
return (
<>
<h1>useCallback</h1>
<p>{count}</p>
<input type="button" value="修改count" onClick={changeCount} />
<hr/>
<SonFn onMyClick={increment} />
</>
)
}
三、总结:
1、“万恶之源” :函数式组件每次重新渲染时,都会把函数体里的所有代码执行一遍。
2、useCallback解决的是 防止函数式组件里的 子函数(闭包) 多次被定义。既就是:useCallback是保证函数式组件重新渲染时,组件里的函数(闭包)只被定义一次
|