useMemo 和 useCallback 简单理解

useMemo 和 useCallback 都是进行性能优化的手段。

某大佬:性能优化总是会有成本的,而且并不总是带来好处。比起花的时间和代码可读性,一点点的性能优化显得微不足道,除了性能重灾区之外,都不值得这么去搞。

useMemo 的使用

export default function WithMemo() {
    const [count, setCount] = useState(1);
    const [val, setValue] = useState('');
    const expensive = useMemo(() => {
        console.log('compute');
        let sum = 0;
        for (let i = 0; i < count * 100; i++) {
            sum += i;
        }
        return sum;
    }, [count]);
 
    return <div>
        <h4>{count}-{expensive}</h4>
        {val}
        <div>
            <button onClick={() => setCount(count + 1)}>+c1</button>
            <input value={val} onChange={event => setValue(event.target.value)}/>
        </div>
    </div>;
}
  1. useMemo 可以作为 Vue 的 computed 来使用。
  2. useMemo 主要用于性能优化,其中的计算比较复杂再使用。
  3. useMemo 具有缓存作用,这里只修改 val,useMemo 返回的 expensive 的值会直接从缓存获取。只有修改 count 的时候才会重新执行得到新的 expensive 的值。

useCallback 的使用

useCallback 和 useMemo 类似,只是缓存的是一个函数。

先来看一个错误的实例:

function Form() {
  const [text, updateText] = useState('');

  const handleSubmit = useCallback(() => {
    console.log(text);
  }, [text]); // 每次 text 变化时 handleSubmit 都会变

  return (
    <>
      <input value={text} onChange={(e) => updateText(e.target.value)} />
      <ExpensiveTree onSubmit={handleSubmit} /> // 很重的组件,不优化会死的那种
    </>
  );
}

每次 input 输入框的改变都会导致 handleSubmit 改变,ExpensiveTree 组件就会重新渲染。

改成下面的方式:

function Form() {
  const [text, updateText] = useState('');
  const textRef = useRef();

  useLayoutEffect(() => {  // 类似 didUpdate
    textRef.current = text; // 将 text 写入到 ref
  });

  const handleSubmit = useCallback(() => {
    const currentText = textRef.current; // 从 ref 中读取 text
    alert(currentText);
  }, [textRef]); // handleSubmit 只会依赖 textRef 的变化。不会在 text 改变时更新

  return (
    <>
      <input value={text} onChange={e => updateText(e.target.value)} />
      <ExpensiveTree onSubmit={handleSubmit} />
    </>
  );
}

这里,我们修改 input 输入框会修改 textRef 的 current,但是 textRef 没有改变,所以 handleSubmit 也就不会改变。那么子组件 ExpensiveTree 也就不会接收到新的 handleSubmit,不会重新渲染了,性能得到了提升。

上一篇:数据库设计中的四个范式


下一篇:Java连接Access数据库的那些坑