根据官网Hooks FAQ给出相关结论:
可以看出,除去极少数特殊情况,都不应该使用 useCallback。在一般情况下对性能上的影响可以忽略不计
那么,useCallback和react.memo具体有哪些用途?
//父组件
export default function () {
let [a, setA] = useState(1)
let [b, setB] = useState(1)
const addA = () => {setA(++a)}
const addB = () => {setB(++b)}
return (
<Fragment>
<div>
<button onClick={addA}>setA</button>
</div>
<div>
<button onClick={addB}>setB</button>
</div>
<div>a:{a}</div>
<div>b:{b}</div>
<Child a={a}></Child>
</Fragment>
);
}
//子组件
const Child = (props: any) => {
useEffect(() => {
console.log("useEffect");
}, [])
let [c, setC] = useState(1)
const add = () => {
setC(++c)
}
return (
<Fragment>
{console.log("render")}
<div style={{ height: "20px" }}></div>
<div style={{ border: "1px solid #ddd" }}>
<div>子组件</div>
<div>
<button onClick={add}>add</button>
</div>
<div>c:{c}</div>
</div>
</Fragment>
);
}
功能上是父组件有两个按钮,其中一个按钮的值会传递给子组件。
现在,当点击setA和setB按钮的时候,都会导致子组件调用render函数,控制台会输出"render"。
但是,由于setA点击会传递参数a给到子组件,子组件会重新渲染。而setB没有值传递给子组件,所以子组件不需要重新渲染,这里就造成了子组件的重新渲染影响了性能。
所以,在这里引入了react.memo。
在父组件不变的情况下。子组件使用memo进行包裹。
const Child = memo((props: any) => {
useEffect(() => {
console.log("useEffect");
}, [])
let [c, setC] = useState(1)
const add = () => {
setC(++c)
}
return (
<Fragment>
{console.log("render")}
<div style={{ height: "20px" }}></div>
<div style={{ border: "1px solid #ddd" }}>
<div>子组件</div>
<div>
<button onClick={add}>add</button>
</div>
<div>c:{c}</div>
</div>
</Fragment>
);
})
这时候就会当点击setA的时候,子组件重新渲染。而点击setB的时候,子组件不重新渲染。
现在,有另外一种情况。父组件增加一个methods方法传递给到子组件。useCallback主要包裹父组件传递给子组件的函数。
export default function () {
let [a, setA] = useState(1)
let [b, setB] = useState(1)
const addA = () => {setA(++a)}
const addB = () => {setB(++b)}
const methods = useCallback(() => {
console.log(123);
}, [])
return (
<Fragment>
<div><button onClick={addA}>setA</button></div>
<div><button onClick={addB}>setB</button></div>
<div>a:{a}</div>
<div>b:{b}</div>
<Child a={a} methods={methods}></Child>
</Fragment>
);
}
这时候memo就不生效了。不管点击setA或setB。都会导致子组件重新渲染。控制台都会输出"render"。
这时候就要用上useCallback了
export default function () {
let [a, setA] = useState(1)
let [b, setB] = useState(1)
const addA = () => {setA(++a)}
const addB = () => {setB(++b)}
const methods = useCallback(() => {
console.log(123);
}, [])
return (
<Fragment>
<div><button onClick={addA}>setA</button></div>
<div><button onClick={addB}>setB</button></div>
<div>a:{a}</div>
<div>b:{b}</div>
<Child a={a} methods={methods}></Child>
</Fragment>
);
}
使用useCallback将传递给子组件的methods方法包裹。这时候点击setA会重新渲染子组件(因为有props传递给子组件),而setB不会重新渲染子组件了。
总结:useCallback一般是和react.memo一起使用的。但是,根据闭包的理解,useCallback传递的是一个闭包函数,所以存在一定的风险。除非是计算量特别大的子组件这种极端情况,否则不推荐使用。
欢迎大家指正,互相学习。