持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
useCallback简单来说就是返回一个函数,只有在依赖项发生变化的时候才会更新(返回一个新的函数)。
场景:设置一个父组件,里面包含它的子组件,并且给这个子组件传父组件拥有的值。我们利用memo,React.memo 为高阶组件。它与 React.PureComponent 非常相似,但它适用于函数组件,但不适用于 class 组件。本质是生成一个性能优化的组件,过滤掉状态未改变时造成子组件不要的声明。
如下列代码,只给子组件传递了count一个属性值,当点击按钮改变父组件的其他三个属性的时候,子组件不会重新渲染,因为用memo包裹了起来。
子组件:
function Child(props){
const {count} = props
console.log("renderChild");
return(
<div>
<div>child :{count}</div>
</div>
)
}
父组件如下:
function App(){
const [count,setCount] = useState(0)
const [id,setId] = useState(1)
const [sex,setSex] = useState("小红")
const [age,setAge] = useState(19)
return(
<div>
<p>App id :{id}</p>
<p>App age :{age}</p>
<p>App sex :{sex}</p>
<button onClick={()=>{setId(id+1)}}>改变用户id</button>
<button onClick={()=>{setAge(age+1)}}>改变用户age</button>
<button onClick={()=>{setSex("小刚")}}>改变用户sex</button>
<button onClick={()=>{setCount(count+1)}}>改变count</button>
<MemoChild count={count} />
</div>
)
}
但是,当给子组件传递一个函数时,如果count没有改变只更改其他三个属性的话,那memo性能优化是不好用的。
function Child(props){
const {count,handleClick} = props
console.log("renderChild");
return(
<div>
<div>child :{count}</div>
<button onClick={
()=>{
handleClick() //点击调用父组件中方法
}
}>count+1</button>
</div>
)
}
const MemoChild = memo(Child)
function App(){
console.log("renderApp");
const [id,setId] = useState(1)
const [age,setAge] = useState(19)
const [sex,setSex] = useState("小红")
const [count,setCount] = useState(0)
//把函数传给子组件调用
const handleClick=()=>{
setCount(count+1);
}
return(
<div>
<p>App id :{id}</p>
<p>App age :{age}</p>
<p>App sex :{sex}</p>
<button onClick={()=>{setId(id+1)}}>改变用户id</button>
<button onClick={()=>{setAge(age+1)}}>改变用户age</button>
<button onClick={()=>{setSex("小刚")}}>改变用户sex</button>
<MemoChild count={count} handleClick={handleClick} /> //传过去
</div>
)
}
同样使用了memo组件,但是却由于是使用传递过来的函数改变count值,这个memo就失效了。
可知,纯状态改变时,memo有用,但函数改变时,memo就失效了。
于是为了改善这个缺陷,我们使用useCallback钩子函数,它可以将这个点击函数包裹起来,可以使当count改变时才可以重新渲染这个子组件,其他三个状态改变无效。
const handleClick=useCallback(()=>{
setCount(count+1);
})
注意:
useCallback钩子函数有几种使用情况。
- 当没有数组时
const handleClick=useCallback(()=>{
setCount(count+1);
})//什么都不写,那么默认只要改变一次父子组件任意就渲染一次子组件
- 当数组占位,却为空时
const handleClick=useCallback(()=>{
setCount(count+1);
},[])//注意看,这里写了[]但是没有赋值什么的
这种情况在useEffect中被视为componentDidMount一种情况,只在加载完页面时调用一次。
当点击其他改变其他三个属性的时候。
当点击改变count时,第一次点击时,子组件进行声明,再次点击,就不改变了。
- 当数组中明确了需要依赖哪一个属性改变时:
const handleClick=useCallback(()=>{
setCount(count+1);
},[count])
只有当count发生改变的时候才会声明子组件,其他三个属性改变都不会影响到子组件的声明。