useCallback 是真的能起到性能优化吗?

42 阅读2分钟

useCallback 接收一个内联回调函数和一个依赖项数组,依赖项改变时才会更新。

使用 useCallback 出现的问题

  1. useCallback 依赖不传 导致代码大片飘红
  2. useCallback 会在依赖不变时缓存该函数,在组件重新render 时 不需要重新的创建。但同时也占用了内存 ,使其不能释放
  3. 在组件内部抛出的回调如果用useCallback 包裹 会出现闭包的问题 难以定位

哪些场景不建议使用useCallback ?

  1. 正常组件的回调函数

    正常组件的回调函数并不能其到优化作用

// 错误⽰例 
function App() { 
   const onChange = useCallback(() => { ... }, [xxx]);
   return ( 
     <input onChange={onChange} /> 
  ); 
}
// 正确⽰例 
function App() { 
   const onChange =() => { ... };
   return ( 
    <input onChange={onChange} /> 
  ); 
}
  1. 自定义组件内的回调(无React.memo或PureComponent 优化时)

如果父组件重新渲染,子组件也必定会重新渲染,如果想要子组件在props未发生变化时不重新进行渲染需要给子组件用memo 进行包裹

eg: 以下自定义组件如果并未用memo 包裹 且嵌套多层 那在父组件渲染时 useCallback 并未起到优化作用,input 组件还是会重新渲染

function Input(props) { 
   return <input onChange={props.onChange} />; 
}

// 错误⽰例
 function App() {
 const onChange = useCallback(() => { ... }, [xxx]);

   return ( 
    <Input onChange={onChange} /> 
   );
}

// 正确⽰例 
function App() { 
const onChange =() => { ... };
  return ( 
    <Input onChange={onChange} /> 
  ); 
} 
  1. 组件内部使用 useCallback 但是依赖没有填写或者填写不正确时
function Input(props) { 
   return <input onChange={props.onChange} />;
 }

// 错误⽰例 
function App() { 
   const [count, setCount] = useState(0);
   // 缺少count依赖,导致只能拿到 0
   const onChange = useCallback(() => { console.log(count); ... }, []); 
   return ( 
     <div>
       <Input onChange={onChange} /> 
     </div>
  ); 
}

// 正确⽰例
 function App() { 
    const [count, setCount] = useState(0);
    const onChange =() => { console.log(count); ... };
      return ( 
       <Input onChange={onChange} /> 
   ); 
}

适合使用useCallback 场景

  1. 当子组件用memo 优化时
React.memo(function Input(props) { 
  return <input onChange={props.onChange} />; 
  }
);
 // 正确⽰例
 function App() { 
   const onChange = useCallback(() => {
   ... 
  }, [xxx]);

  return ( <Input onChange={onChange} /> 
); }
  1. 当有多个地方调用同一块逻辑,并且有依赖项时
function App() { 

const onChange = useCallback(() => {
 ... 
}, [xxx]);

 useEffect(() => { 
  onChange();
},[onChange]); 

   return ( 
    <Input onChange={onChange} />
   );
 }