先来看一个例子:
import React, { PureComponent, useState } from "react";
class Child extends PureComponent {
render() {
console.log("child render");
return (
<>
<div>{this.props.txt}</div>
<button onClick={this.props.onClick}>改变txt</button>
</>
);
}
}
export default function App() {
console.log("App render");
const [txt, setTxt] = useState("abc");
return (
<Child
txt={txt}
onClick={() => {
setTxt(Math.random());
}}
></Child>
);
}
注意,子组件是类组件,并且使用了 PureComponent
,所以每次 props
改变,也就是点击按钮都会输出:
App render
child render
现在修改父组件,增加了一个状态的改变:
export default function App() {
console.log("App render");
const [txt, setTxt] = useState("abc");
const [n, setN] = useState(0);
return (
<>
<Child
txt={txt}
onClick={() => {
setTxt(Math.random());
}}
></Child>
<input
type="number"
value={n}
onChange={(e) => {
setN(e.target.value);
}}
/>
</>
);
}
理论上来说,状态变量 n
发生变化时,父组件会重新渲染打印 App render
没有问题。但子组件的不会有打印,因为传递给它的状态没有改变。
可状态变量 n
发生变化时,会打印:
App render
child render
其实问题出现在 this.props.onClick
上了,注意到传递给它的是一个函数,而在父组件重新渲染时,会产生一个新的函数。
此时对子组件来说,函数的引用地址发生了变化,所以 PureComponent
“失效了”。
<Child
txt={txt}
onClick={() => {
setTxt(Math.random());
}}
></Child>
接着会发现,无论怎么做,传递给子组件的都是一个新的函数:
写法1:
export default function App() {
// ...
// App 组件重新调用,会重新生成新函数。
function handleClick() {
setTxt(Math.random());
}
return (
<>
<Child txt={txt} onClick={handleClick}></Child>
{ // ... }
</>
);
}
写法2:
每次还是新的函数。
function handleClick(setTxt) {
setTxt(Math.random());
}
export default function App() {
// ...
return (
<>
<Child txt={txt} onClick={() => handleClick(setTxt)}></Child>
{ // ... }
</>
);
}
所以,需要一个方法,能够将函数的引用值固定,不要每次渲染都是新函数,才能解决这个问题。
作用:用于得到一个固定引用值的函数。通常用它来进行性能优化。
使用:接收2个参数,
useCallBack
会固定该函数的引用,只要依赖项(参数2)没有发生变化,则使用返回之前的函数地址。可以看到,和 useEffect 很像。依赖项不发生变化,第1个函数不会再次执行。
import React, { useCallback } from "react";
export default function App() {
// ...
const handleClick = useCallback(() => {
setTxt(Math.random());
}, []);
return (
<>
<Child txt={txt} onClick={handleClick}></Child>
{ // ... }
</>
);
}
第2个参数传不传 [txt]
效果是一样的。
因为如果传递 txt
,则只有 txt
发生变化,才会返回新函数。
以上。