美文网首页
useCallback

useCallback

作者: 就问你怕不怕 | 来源:发表于2021-12-13 15:50 被阅读0次

useCallback同前面讲的useMemo、useLayoutEffect一样可以用来提高性能。
先来看看官网如何解释:


image.png

官网的解释很简单,其中有关键的两点:

  1. useCallback返回回调函数的memoized版本
  2. 相当于useMemo

也就是说useCallback得到的是传入它的回调函数,

下面来看看它解决的是什么样的场景:

import { useState, useCallback, memo } from "react";
import * as ReactDOM from "react-dom";

const Child = memo(function({val, onChange}) {
  console.log('render...');
  return <input value={val} onChange={onChange} />;
});

function App() {
  const [val1, setVal1] = useState('');
  const [val2, setVal2] = useState('');
  // 每次父组件渲染,返回的是不同的函数引用
  const onChange1 = evt => {
    setVal1(evt.target.value);
  };
  const onChange2 = evt => {
    setVal2(evt.target.value);
  };

  return (
  <>
    <Child val={val1} onChange={onChange1}/>
    <Child val={val2} onChange={onChange2}/>
  </>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
image.png

我在其中一个Child中输入,却同时引起了另一个Child的渲染,分析产生这个问题的原因(以在第一个Child中输入为例):

  1. val1改变导致App渲染
  2. App渲染,导致生成新的onChange1以及 onChange2,传给Child
  3. 两个Child发现自己onChange的引用变了,重新渲染

这种情况是很常见的,平时我们的代码基本上都存在这样的问题,一个父组件中必定会引用各种各样的子组件,父组件一旦渲染势必会引起它所有子组件也跟随渲染。但是如果onChange事件在这里不再是一个普通的函数而是一个耗费大量计算的函数,那么还是得注意到这方面的性能优化的。
这个问题该如何解决呢?
使用useCallback将onChange事件包裹起来即可:

   // 每次父组件渲染,返回的是同一个函数引用
  const onChange1 = useCallback( evt => {
    setVal1(evt.target.value);
  }, []);

  const onChange2 = useCallback( evt => {
    setVal2(evt.target.value);
  }, []);
image.png

可以看到输入多少次打印多少次。
Child组件之间不再互相影响,原因就是依赖项为空数组,那么只在初始化的时候回调函数给到onChange1/onChange2变量,即使父组件App重新渲染,在useCallback的包装下给到Child的永远都是memoized版本,因此Child组件不会发生重新渲染。

另一个demo:debounce

debounce在项目中经常被使用,当我们从class组件切换到函数式组件的时候发现debounce的功能失效了,失效的原因通常是我们依照Lodash.debounce的官网所示用法简单的将需要防抖处理的函数放入debounce的回调函数,这样做在正常的运行环境中是没有问题的,但是,react组件在每次update后内部声明的函数就会重新创建,因此对这些函数的引用也就被改变,debounce回调函数也就每次被置入新的引用,debounce对其回调函数执行时机的控制也就失效了。而正好useCallback能够保留函数的memoized版本,也就使得debounce保留了update前对同一个函数的引用:

  const [value, setInputValue] = React.useState("");
  const [query, setSearchQuery] = React.useState("");
  //在函数组件中用useCallback来确保不同生命周期内函数引用不变
  const searchHandle = useCallback(val => {
    console.log(val);
    setSearchQuery(val);
  }, [])
  // 这里也要提前将debounce成品提出,不得放在handleChange
  const debounceSearchQuery = useCallback(debounce(searchHandle, 300), []);

  const handleChange = (e) => {
    setInputValue(e.target.value);
    debounceSearchQuery(e.target.value);
  };

  <input onChange={handleChange} placeholder="输入搜索内容" value={value} />
  // 子组件:
  <NewList query={query} />

其实在class组件中也是将debounce提前声明到constructor的做法来保留对回调函数的引用:

constructor(props) {
   super(props);
   this.inputChange = debounce(this.inputChange, 500);
}

与useEffect

前面官网讲到useCallback与useMemo相当,其实质都是返回一个memoized的结果,只是useCallback返回的是一个函数,useMemo返回的是一个值(简单数据类型或引用类型)。

相关文章

  • 【web前端】useCallback 和 useMemo 的用法

    useCallback 使用useCallback可以避免组件重复渲染,提升性能,以下是一个使用useCallba...

  • useMemo和useCallback

    参考文章:链接 useMemo useCallback

  • useCallback - React源码解析(五)

    本文将讲解useCallback的源码实现。useCallback是也一个非常简单的钩子,其实现比useMemo还...

  • useCallback

    1. 问题引发 子组件onChange调用了父组件的handleOnChange 父组件handleOnChang...

  • useCallback

    简介 1 .优化子组件的渲染次数2 .父组件重新渲染组件的时候,传给子组件的函数类型的props也会重新生成,所以...

  • useCallback

    常见应用场景:当父组件传值或传递函数给子组件时,适合通过useMemo或useCallback将子组件的props...

  • useCallback

    一、前言 对于新手来说,没写过几次死循环的代码都不好意思说自己用过 React Hooks。本文将以useCall...

  • useCallback

    useCallback同前面讲的useMemo、useLayoutEffect一样可以用来提高性能。先来看看官网如...

  • react不同钩子useCallback与 useMeno

    useCallback性能优化的点: 1.当需要将一个函数传递给子组件时, 最好使用useCallback进行优化...

  • react的useMemo

    react的useMemouseMemo与useCallback使用指南

网友评论

      本文标题:useCallback

      本文链接:https://www.haomeiwen.com/subject/gebtfrtx.html