当前位置: 首页 > article >正文

react中的useCallback、useMemo、useRef 和 useContext

hook函数中依赖项:函数中使用的响应式变量组成的数组。响应式变量包括 props、state 和所有你直接在组件中定义的变量和函数。

前言

一、useCallback  缓存回调函数

使用方式 

二、useMemo:缓存计算的结果

三、useRef:在多次渲染之间共享数据

四、useContext:定义全局状态 

总结


前言

上一个文章中我们讲述了useState 和 useEffect 这两个最为核心的 Hooks 的用法。理解了它们,基本上就掌握了 React 函数组件的开发思路。


在 React 函数组件中,每一次 UI 的变化,都是通过重新执行整个函数来完成的,这和传统的 Class 组件有很大区别:函数组件中并没有一个直接的方式在多次渲染之间维持一个状态。

一、useCallback  缓存回调函数

比如下面的代码中,我们在加号按钮上定义了一个事件处理函数,用来让计数器加 1。但是因为定义是在函数组件内部,因此在多次渲染之间,是无法重用 handleIncrement 这个函数的,而是每次都需要创建一个新的:

使用方式 

function Counter() {
  const [count, setCount] = useState(0);
  const handleIncrement = () => setCount(count + 1);
  // ...
  return <button onClick={handleIncrement}>+</button>
}
useCallback(fn, deps)

不妨思考下这个过程。每次组件状态发生变化的时候,函数组件实际上都会重新执行一遍。在每次执行的时候,实际上都会创建一个新的事件处理函数 handleIncrement。这个事件处理函数中呢,包含了 count 这个变量的闭包,以确保每次能够得到正确的结果。

这也意味着,即使 count 没有发生变化,但是函数组件因为其它状态发生变化而重新渲染时,这种写法也会每次创建一个新的函数。创建一个新的事件处理函数,虽然不影响结果的正确性,但其实是没必要的。因为这样做不仅增加了系统的开销,更重要的是:每次创建新函数的方式会让接收事件处理函数的组件,需要重新渲染。

比如这个例子中的 button 组件,接收了 handleIncrement ,并作为一个属性。如果每次都是一个新的,那么这个 React 就会认为这个组件的 props 发生了变化,从而必须重新渲染。因此,我们需要做到的是:只有当 count 发生变化时,我们才需要重新定一个回调函数。而这正是 useCallback 这个 Hook 的作用。

修改后

import React, { useState, useCallback } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const handleIncrement = useCallback(
    () => setCount(count + 1),
    [count], // 只有当 count 发生变化时,才会重新创建回调函数
  );
  // ...
  return <button onClick={handleIncrement}>+</button>
}

这样,只有 count 发生变化的时候,才需要重新创建一个回调函数,这样就保证了组件不会创建重复的回调函数。而接收这个回调函数作为属性的组件,也不会频繁地需要重新渲染。

二、useMemo:缓存计算的结果

代码如下(示例):

useMemo(fn, deps);

这里的 fn 是产生所需数据的一个计算函数。通常来说,fn 会使用 deps 中声明的一些变量来生成一个结果,用来渲染出最终的 UI。

这个场景应该很容易理解:如果某个数据是通过其它数据计算得到的,那么只有当用到的数据,也就是依赖的数据发生变化的时候,才应该需要重新计算。 

通过 useMemo 这个 Hook,可以避免在用到的数据没发生变化时进行的重复计算。如果是一个复杂的计算,那么对于提升性能会有很大的帮助。这也是 userMemo 的一大好处:避免重复计算

除了避免重复计算之外,useMemo 还有一个很重要的好处:避免子组件的重复渲染。如果一个子组件的属性的值被缓存了,一旦能够缓存上次的结果,就和 useCallback 的场景一样,可以避免很多不必要的组件刷新。

useCallback 的功能其实是可以用 useMemo 来实现的。

 const myEventHandler = useMemo(() => {
   // 返回一个函数作为缓存结果
   return () => {
     // 在这里进行事件处理
   }
 }, [dep1, dep2]);

从本质上来说,它们只是做了同一件事情:建立了一个绑定某个结果到依赖数据的关系。只有当依赖变了,这个结果才需要被重新得到。

三、useRef:在多次渲染之间共享数据

函数组件虽然非常直观,简化了思考 UI 实现的逻辑,但是比起 Class 组件,还缺少了一个很重要的能力:在多次渲染之间共享数据。

在类组件中,我们可以定义类的成员变量,以便能在对象上通过成员属性去保存一些数据。但是在函数组件中,是没有这样一个空间去保存数据的。因此,React 让 useRef 这样一个 Hook 来提供这样的功能。

const myRefContainer = useRef(initialValue);

使用 useRef 保存的数据一般是和 UI 的渲染无关的,因此当 ref 的值发生变化时,是不会触发组件的重新渲染的,这也是 useRef 区别于 useState 的地方。

除了存储跨渲染的数据之外,useRef 还有一个重要的功能,就是保存某个 DOM 节点的引用。

四、useContext:定义全局状态 

React 组件之间的状态传递只有一种方式,那就是通过 props。这就意味着这种传递关系只能在父子组件之间进行。

看到这里你肯定会问,如果要跨层次,或者同层的组件之间要进行数据的共享,那应该如何去实现呢?这其实就涉及到一个新的命题:全局状态管理。为此,React 提供了 Context 这样一个机制,能够让所有在某个组件开始的组件树上创建一个 Context。

这样这个组件树上的所有组件,就都能访问和修改这个 Context 了。那么在函数组件里,我们就可以使用 useContext 这样一个 Hook 来管理 Context。

const value = useContext(MyContext);

Context 提供了一个方便在多个组件之间共享数据的机制。不过需要注意的是,它的灵活性也是一柄双刃剑。你或许已经发现,Context 相当于提供了一个定义 React 世界中全局变量的机制,而全局变量则意味着两点:

  1. 会让调试变得困难,因为你很难跟踪某个 Context 的变化究竟是如何产生的。
  2. 让组件的复用变得困难,因为一个组件如果使用了某个 Context,它就必须确保被用到的地方一定有这个 Context 的 Provider 在其父组件的路径上。

所以在 React 的开发中,除了像 Theme、Language 等一目了然的需要全局设置的变量外,我们很少会使用 Context 来做太多数据的共享。需要再三强调的是,Context 更多的是提供了一个强大的机制,让 React 应用具备定义全局的响应式数据的能力。


总结

最后来总结一下今天的所学。在这节课,你看到了 4 个常用的 React 内置 Hooks 的用法,包括:useCallback、useMemo、useRef 和 useContext。

事实上,每一个 Hook 都是为了解决函数组件中遇到的特定问题。因为函数组件首先定义了一个简单的模式来创建组件,但与此同时也暴露出了一定的问题。所以这些问题就要通过 Hooks 这样一个统一的机制去解决,可以称得上是一个非常完美的设计了。


http://www.kler.cn/a/279883.html

相关文章:

  • 京存分布式赋能EDA应用
  • 安卓中回调函数的使用
  • 一文读懂红鲸音视频SDK
  • 程序员职业转型难题解析:为何不尝试投身大模型开发的新赛道?
  • 区块链(币圈)常用网址大全
  • Java-BatchProcessingUtil结合CompletableFuture工具类
  • JDK17 隐藏类 Hidden Classes 介绍
  • 使用VScode的Git版本控制功能(图文版)
  • SQL语法:create、insert、update、
  • yolov8 安装流程
  • 基于pygame的雷电战机小游戏
  • c++每日练习记录5-(链表的结尾指向nullptr)
  • 如何用Hive进行高校考试分析:大数据技术提升教育质量
  • 如何基于向量数据库+LLM(大语言模型)打造企业专属Chatbot?
  • 大数据系统测试——大数据系统解析(下)
  • 【区块链 + 司法存证】神州契信区块链电子签约系统 | FISCO BCOS应用案例
  • 500元以内蓝牙耳机什么牌子好?四个百元爆款耳机品牌大盘点
  • 国内使用tensorflow_datasets加载数据
  • HTML静态网页成品作业(HTML+CSS)——西点蛋糕介绍(5个页面)
  • fastapi知识点及应用