ReactuseCallback闭包陷阱
1。为什么需要useCallback在使用useCallback和useMemohooks之前,如果每次都修改parent的count,那么child会被重新渲染constChild=(props)=>{console.log('Childrender');return(
子点击
)}constparent=()=>{const[count,setCount]=useState(1);constcb=()=>{console.log(count);};return(
parentsetCount()}>click
)}2.在场景重现功能组件中,一般使用useCallback和useMemo来模拟componentShouldUpdatehook,避免不必要的渲染。经过优化,我们发现在parent中,常量setCount修改状态不会触发Child组件的更新。似乎解决了父功能组件更新状态,导致子组件无谓更新的问题。但是当你点击子组件的按钮时,你会发现count的初始值为1,这是useCallback的闭包陷阱,无法获取到最新状态。constChild=(props)=>{console.log('Childrender');return(
子点击
)}constparent=()=>{const[count,setCount]=useState(1);constcb=React.useCallback(()=>{console.log(count);},[]);constChildMemo=React.useMemo(()=>
,[cb]);return(
父级计数:{count}setCount(count+1)}>click{ChildMemo} )}3.有两种解决方案:两种方法都将回调保存在一个引用对象中。不同的是方法2不需要修改子组件,但是也增加了理解成本。放弃useCallback,使用useRefhook,使用ref.current来引用箭头函数。使用useRef还需要修改子组件回调的调用。constChild=(props)=>{console.log('Childrender');return(
{props.callbackRef.current();}}>子点击
)}constparent=()=>{const[count,setCount]=useState(1);constcbRef=React.useRef(null);cbRef.current=()=>{console.log(count);};constChildMemo=React.useMemo(()=>
,[]);return(
父级计数:{count}setCount(count+1)}>click{ChildMemo} )}自定义一个hook,使用useCallback搭配useRefconstuseImmediateValue=(cb)=>{constref=useRef(cb);ref.current=cb;constval=useCallback(()=>{ref.current();},[]);returnval;}constChild=(props)=>{console.log('Childrender');return(
)}constparent=()=>{const[count,setCount]=useState(1);constcb=useImmediateValue(()=>{console.log(count);})constChildMemo=React.useMemo(()=>