第七章 React Hooks之useRef、useMemo、useCallback

一、专栏介绍 🌟🌟

欢迎加入本专栏!本专栏将引领您快速上手React,让我们一起放弃放弃的念头,开始学习之旅吧!我们将从搭建React项目开始,逐步深入讲解最核心的hooks,以及React路由、请求、组件封装以及UI(Ant Design)框架的使用。让我们一起掌握React,开启前端开发的全新篇章!

二、useRef 💫💫

2.1、它是什么 👇👇

useRef是一个React Hook,它能帮助引用一个不需要渲染的值。还可以通过它操作 DOM。

2.2、demo1 不需要渲染的值 👇👇

useRef 是一种用于在React函数组件中创建持久化(即不会被重新渲染)引用的Hook。这意味着它可以帮助我们获取到那些不需要渲染的值并进行直接操作。不同于 useState,useRef 存储的是不会改变的引用值,这些值在组件生命周期内保持不变。 useRef 返回一个只有一个属性的对象,其属性名为 current。与 useState 不同,我们可以通过直接修改 current 的值来改变 useRef 的引用。尽管如此,我们需要注意的是 useRef 的值在程序中是可变的,并且它是同步的,但与 useState 不同,它不会因为值的改变而导致页面重新渲染。 总的来说,useRef 是我们存储和操作那些不需要重新渲染的值的理想方式。

所以下面的例子中,p标签中的值不会因为count.current的变化而刷新。但它在函数里面是实时刷新的。

TypeScript 复制代码
import React, { useRef } from 'react';

function UseRefDemo() {
  const count = useRef(0);

  return (
    <>
      <p>{count.current}</p>
      <button
        onClick={() => {
          count.current += 1;
          alert(count.current);
        }}
      >
        count+1
      </button>
    </>
  );
}

export default UseRefDemo;

2.3、demo2 不需要渲染的值 👇👇

在上面的例子中,虽然可以使用其他方法如 useState、useEffect 来模拟类似 useRef 的效果并使页面刷新,但在实际开发中,应根据具体需求来选择使用哪种方法。过多地使用 useRef 可能会使代码结构变得复杂,不利于后期维护。

因此,建议在使用 useRef 时要根据具体需求来评估是否真的需要它。如果只是为了存储一个值并且不需要触发重渲染,那么使用 useState 或其他更适合的方法可能更为合适。这样可以保持代码的清晰度和可维护性。

请注意,使用 useRef 并非总是必要的,要根据具体情况来决定是否使用它。

TypeScript 复制代码
import React, { useRef, useState } from 'react';

function UseRefDemo() {
  const count = useRef(0);

  const [isShow, setIsShow] = useState<boolean>(false);

  return (
    <>
      {isShow && <p>您已经点击超过{count.current - 1}了</p>}
      <button
        onClick={() => {
          count.current += 1;
          if (count.current > 10) {
            return setIsShow(true);
          }
        }}
      >
        点击+1
      </button>
    </>
  );
}

2.4、demo3 操作DOM 👇👇

操作DOM比较简单,和vue差不多一样,就不做过多说明,直接上例子。

TypeScript 复制代码
import React, { useRef } from 'react';

function UseRefDemo() {
  // 通过 ref 操作 DOM
  // 首先,声明一个初始值为null的ref对象,并将它赋值给input输入框的ref属性
  const inputRef: React.MutableRefObject<HTMLInputElement | null> = useRef(null);

  // 通过ref实现聚焦输入框
  const handleClick = () => {
    // 上面如果没有定义inputRef: React.MutableRefObject<HTMLInputElement | null>,就可以使用下面注释起来的的写法
    // (inputRef.current as unknown as HTMLInputElement).focus();
    inputRef.current?.focus?.();
  };

  const getInputValue = () => {
    console.log('👉👉👉-----------------输入的值为:', inputRef.current?.value);
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>聚焦输入框</button>
      <button onClick={getInputValue}>获取输入的值</button>
    </>
  );
}
export default UseRefDemo;

三、useMemo 💥💥

useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。

函数组件中只要state更新就会重新渲染页面,而useMemo整好可以解决这个问题。它可以缓存数据,不用每次执行函数都重新渲染页面,通常我们会在一些计算量比较大业务场景中使用,以达到提升性能的效果。

写法上面和useEffect很像,但是他的第一个参数比如返回一个结果。

const cachedValue = useMemo(calculateValue, dependencies)

calculateValue:要缓存计算值的函数。它应该是一个没有任何参数的纯函数,并且可以返回任意类型。

React 将会在首次渲染时调用该函数;在之后的渲染中,如果 dependencies 没有发生变化,

React 将直接返回相同值。否则,将会再次调用 calculateValue 并返回最新结果,然后缓存该结果以便下次重复使用。

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

如果你在代码检查工具中 配置了 React,它将会确保每一个响应式数据都被正确地定义为依赖项。

依赖项数组的长度必须是固定的并且必须写成 dep1, dep2, dep3 这种形式。React 使用 Object.is 将每个依赖项与其之前的值进行比较。

3.1、不使用useMemo 👇👇

你可以把我的代码拷贝到编辑器中,运行后在浏览器中观察,点击按钮后控制的打印效果。

TypeScript 复制代码
import React, { useState } from 'react';

const getTotal = (count: number) => {
  console.log('👉👉👉-----------------重新渲染了');
  return count * 5;
};

function UseMemoDemo() {
  // 写法上面和useEffect很像啊

  // const cachedValue = useMemo(calculateValue, dependencies)

  const [isShow, setIsShow] = useState(false);

  const [count, setCount] = useState(0);

  const total = getTotal(count);

  return (
    <>
      <div>
        <p>当前的值是{total}</p>
        {isShow && <div>文字段落显示了</div>}
        <button onClick={() => setIsShow((show) => !show)}>{isShow ? '隐藏' : '显示'}</button>
        <button onClick={() => setCount((count) => count + 1)}>改变值</button>
      </div>
    </>
  );
}
export default UseMemoDemo;

3.2、使用useMemo后 👇👇

TypeScript 复制代码
import React, { useMemo, useState } from 'react';

const getTotal = (count: number) => {
  console.log('👉👉👉-----------------重新渲染了');
  return count * 5;
};

function UseMemoDemo() {
  // 写法上面和useEffect很像啊

  // const cachedValue = useMemo(calculateValue, dependencies)

  const [isShow, setIsShow] = useState(false);

  const [count, setCount] = useState(0);

  // const total = getTotal(count);

  const total = useMemo(() => {
    return getTotal(count);
  }, [count]);

  return (
    <>
      <div>
        <p>当前的值是{total}</p>
        {isShow && <div>文字段落显示了</div>}
        <button onClick={() => setIsShow((show) => !show)}>{isShow ? '隐藏' : '显示'}</button>
        <button onClick={() => setCount((count) => count + 1)}>改变值</button>
      </div>
    </>
  );
}
export default UseMemoDemo;

四、useCallback 💦💦

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。写法和useEffect差不多。确实,对于初次接触 React Hook 的开发者来说,useCallback 可能会带来一些困惑。通过下面的例子,我们可以直观地比较使用和不使用 useCallback 的情况,以更好地理解其功能和用法。

不使用useCallback

TypeScript 复制代码
import React, { useCallback, useState } from 'react';

function UseCallbackDemo() {
  let i = 0;

  const [count, setCount] = useState(0);

  const handleClick = () => {
    i++;
    console.log(`点击了${i}次按钮`);
    setCount(count + 1);
  };

  return (
    <div>
      <p>计数器: {count}</p>
      <button onClick={handleClick}>点击我</button>
    </div>
  );
}

export default UseCallbackDemo;

使用useCallback

TypeScript 复制代码
import React, { useState, useCallback } from 'react';

function UseCallbackDemo() {
  let i = 0;

  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    i++;
    console.log(`点击了${i}次按钮`);
    setCount((count) => count + 1);
  }, []);

  return (
    <div>
      <p>计数器: {count}</p>
      <button onClick={handleClick}>点击我</button>
    </div>
  );
}

export default UseCallbackDemo;
TypeScript 复制代码
const handleClick = useCallback(() => {
  i++;
  console.log(`点击了${i}次按钮`);
  setCount((count) => count + 1);
}, []);

上面的列子中,依赖项数组为空,这意味着这些函数的引用不会改变,即使组件重新渲染也是如此。这意味着在多次点击按钮时,React 将继续使用相同的函数实例,而不是创建新的函数实例。这可以避免不必要的重新渲染和性能开销。

五、总结 💪💪

useRef、useMemo、useCallback这些React Hook在我们开发过程中都具有重要的意义。

useRef是一个用于保存组件内部可变数据的Hook。通过使用useRef,我们可以在组件渲染周期内保存数据,并在组件重新渲染时保持数据不变。这在一些场景中非常有用,例如记录上一次渲染的一些值,或者在组件生命周期中保持一些状态的不变性。

useMemo是一个用于优化性能的Hook。在复杂的React应用中,我们可能会遇到组件频繁重新渲染的情况,这可能会导致应用性能下降。useMemo可以帮助我们避免在每次组件重新渲染时都执行不必要的计算或渲染。通过将计算或渲染的结果缓存起来,useMemo只在依赖项发生变化时才返回新的结果,从而提高了应用的性能。

useCallback是一个用于缓存需要在组件中多次调用的回调函数的Hook。通过使用useCallback,我们可以在每次组件渲染时返回相同的回调函数实例,避免了在每次渲染时都重新生成回调函数,从而提高组件的性能。这对于避免不必要的重新渲染和避免在每次渲染时都创建新的函数实例非常有帮助。

总的来说,useRef、useMemo和useCallback这些React Hook为我们提供了更灵活、高效和优化的开发方式,使我们在开发React应用时能够更好地控制组件的行为和性能。

👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇

相关推荐
李纲明1 小时前
微信小程序 getSystemInfo 提示已废弃、屏幕高度算错胶囊还对不齐?getWindowInfo 和 getDeviceInfo 怎么接才稳
javascript·微信小程序·小程序·小程序开发·devdiy
Felven2 小时前
C. Brr Brrr Patapim
c语言·开发语言
FungLeo2 小时前
成为全栈·Vue 管理后台篇·用 fetch 建一层可控的请求内核
前端·javascript·vue.js·http·fetch·formdata·接口封装
用户84298142418102 小时前
ByteJS,让重要的 JavaScript 以字节码运行。
javascript
路溪非溪3 小时前
python标准库使用总结
开发语言·python
FungLeo4 小时前
成为全栈·Vue 管理后台篇·Vue Router如何守住多角色后台边界
前端·javascript·vue.js·rbac·vue router·路由守卫·前端权限
FungLeo5 小时前
成为全栈·Vue管理后台篇·仪表盘不是几张卡片:组合统计接口与角色视图
前端·javascript·vue.js·rbac·仪表盘·管理后台·tanstack query
程序员大阳5 小时前
HTML+CSS+JS对话式实战(7)-学生端活动报名功能开发
javascript·css·html·活动报名
SomeOtherTime5 小时前
Rust 面向对象介绍(AI回答)
开发语言·python·rust