React Hooks 入门下

2023-10-27

前面的话

上篇介绍了 useStateuseEffect 两个钩子函数,这篇将接着介绍其他常用的钩子函数。

1、useCallback

作用:

  • 该 hooks 返回一个 memoized 回调函数,根据依赖项来决定是否更新函数。
  • 其依赖项可以为一个空数组, 表示没有依赖值, 将永远不会被更新。
  • useCallback 可以优化代码,防止不必要的渲染。但也并非所有的方法都要用 useCallback 去包裹一层。

使用例子

useCallbackReact.memo 完美搭配:

React.memo 的作用:只适用于函数组件,能对 props 做浅比较,可以防止组件无效的重复渲染。

场景: 一个父组件,包含一个子组件,子组件接受一个函数作为 props;一般来说,如果父组件更新了,子组件也会更新,但大多数情况,子组件的更新是没有必要的,可以借助 useCallback 来返回一个函数,然后把这个函数作为 props 传递给子组件;这样子组件就可以避免不必要的更新渲染。

interface Props {
  onClickButton: () => void;
  children: string;
}

// 父组件
const Parent = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  const handleClickButton1 = () => {
    setCount1(count1 + 1);
  };

  const handleClickButton2 = useCallback(() => {
    setCount2(count2 + 1);
  }, [count2]);

  return (
    <>
      <div>
        <Button onClickButton={handleClickButton1}>Button1</Button>
      </div>
      <div>
        <Button onClickButton={handleClickButton2}>Button2</Button>
      </div>
    </>
  );
};
// 子组件 
const Button = React.memo(({onClickButton, children}: Props) => {
  return (
    <>
      <button onClick={onClickButton}>{children}</button>
      <span>{Math.random()}</span>
    </>
  );
});

点击上述两个按钮会发现,点击 Button1 时只会更新Button1 后面的nearing,点击 Button2 时,两个按钮后面的内容都更新了。

为什么点击 Button2 时也会导致 Button1 后面的内容更新,尽管子组件使用了React.memo也无效。

那是由于 React.memo 是浅比较, 当点击 Button2 时导致父组件重新渲染,handleClickButton1这个函数虽与渲染前一样没有发生变化,但是两个一样的函数是不想等的,这样 React.memo 在比较是发现两个函数不想等,所以还是会重新更新。

为什么点击 Button1 时不会重新渲染 Button2 的内容?

由于 handleClickButton2 函数使用 useCallback 包裹,并且还依赖 count2 这个变量,这里 useCallback 会根据 count2 是否发生变化,从而决定是否返回一个新的函数,函数内部作用域也会随之跟新。所以点击 Button1 不会重新渲染 Button2 的内容。

2、useMemo

useCallback 是用来返回一个 memoized 函数,而 useMemo 用来返回一个 memorized 的值。

作用:

  • 用于缓存昂贵的计算,避免在每次渲染时都进行高开销的计算
  • 减少子组件不必要的渲染
  • 并非所有的变量值都采用 useMemo 做缓存优化

使用例子1

缓存昂贵的计算:

const num = useMemo(()=> {
   let num;
   // 根据 defaultValue 来对 num 进行复杂的计算
   //...
   return num;
},[defaultValue])

使用例子2

useMemo 与 React.memo 完美搭配:

interface Data {
  name: string;
}
interface Props {
  data: Data;
}

// 父组件
const Parent = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('xiaoqi');

  const handleClickButton1 = () => {
    setCount(count + 1);
  };

  const handleClickButton2 = (name: string) => {
    setName(name);
  };

  const data = {
    name,
  };

  return (
    <>
      <div>
        <p>{count}</p>
        <button onClick={handleClickButton1}>update count</button>
        <button onClick={() => handleClickButton2('rose')}> update name</button>
      </div>
      <Child data={data} />
    </>
  );
};


// 子组件
const Child = React.memo(({data}: Props) => {
  return (
    <>
      <p>child</p>
      <p>
        {data.name} {Math.random()}
      </p>
    </>
  );
});

可以发现,当点击按钮1时,子组件也会重新渲染。原因就是 React.memo 是浅比较,而每次父组件渲染后都会又一个新的对象data,尽管只一样,但是地址不一样,还是会重新渲染子组件。

将 data 使用 useMemo 来包裹, 依赖值是 name ,这样点击按钮1,子组件不会重新渲染。

const data = useMemo(() => {
  return {
    name,
  };
},[name]);

⚠️注意:

  • 不要过度依赖 useMemo,useMemo 本身有一定的开销。useMemo会记住一些值,在后续的render中,将依赖数组中的值取出来和上一次记录的值进行比较,如果不相等才会重新执行回调函数,否则直接返回所记住的值。这个过程本身就会消耗一定的内存和计算资源。因此,过度使用 useMemo 可能会影响程序的性能。

  • 使用 useMemo 前,应该考虑:

    1、传递给 useMemo 的函数开销大不大? 如果计算开销很大,我们就要记住它的返回值。但对于简单的 js 运算,不需要使用 useMemo 来记住它的返回值。

    2、如果返回值是引用类型,并且它被当作props传递给子组件,那么需要使用 useMemo 来记住这个返回值。否则尽管这个引用类型的值没有发生变化,但是地址会发生变化,React.memo 也会判断其不相等。

3、useRef

useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数。返回的对象在组件的整个周期内保持不变。

useRef 创建的对象与自建的 {current: …} 对象区别是每次渲染时,useEef 返回的是同一个对象。

const testRef = useRef(initialValue);

作用:

  • 获取 Dom 元素的节点
  • 获取组件实例
  • 渲染周期之后共享数据的存储

使用例子1:

interface ChildProps {
  num: string;
}
class Child extends React.Component<ChildProps> {
  render() {
    const {num} = this.props;
    return <h2>count: {num}</h2>;
  }
}

const Example = () => {
  const [count, setCount] = useState(0);
  const inputRef = useRef(null);
  const childRef = useRef(null);

  useEffect(() => {
    console.log(inputRef.current);
    console.log(childRef.current);
  }, []);

  return (
    <div>
      <input ref={inputRef} type='text' />
      <p>{count}</p>
      <button onClick={() => setCount(count => count + 1)}>setCount</button>
      <Child num='1' ref={childRef} />
    </div>
  );
};

上面的例子中获取 Dom 元素的节点和组件实例。这里子组件使用 class 写法的原因是:函数组件没有实例,如果想使用 ref 来获取组件实例,子组件就要使用 class 的形式。
在这里插入图片描述

使用例子2

对于 useState 我们知道,每次 set 之后就会重新渲染组件,但如果我们想在 set 之后,立即获得新的 state ,我们可以使用 useRef 来保存、修改、读取。

const Example = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);
  const childRef = useRef(null);

  useEffect(() => {
    console.log(childRef.current);
  }, []);

  const handleClick = () => {
    const newCount = count + 1;
    setCount(newCount);
    countRef.current = newCount;
  };

   
  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>setCount</button>
      <Child num='1' ref={childRef} />
    </div>
  );
};

如果我们想获取子组件里的 Dom 元素节点, 可以使用 React.forwardRef:

const Child = React.forwardRef(
  (
    props: ChildProps,
    ref:
      | string
      | ((instance: HTMLHeadingElement | null) => void)
      | React.RefObject<HTMLHeadingElement>
      | null
      | undefined
  ) => {
    const {num} = props;
    return <h2 ref={ref}>count: {num}</h2>;
  }
);


const Example = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(0);
  const childRef = useRef(null);

  useEffect(() => {
    console.log(childRef.current);
  }, []);

  const handleClick = () => {
    const newCount = count + 1;
    setCount(newCount);
    countRef.current = newCount;
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>setCount</button>
      <Child num='1' ref={childRef} />
    </div>
  );
};

此时 childRef.current 指向的是子组件中的 DOM 元素 H2:
在这里插入图片描述

4、useContext

const MyContext = React.createContext();
const value = useContext(MyContext);
  • useContext 接受一个 context 对象(React.createContext 的返回值) 并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
  • CreateContext 接受一个参数为 value prop 的初始值。

5、useReducer

useReducer 是 useState 的替代方案,当需要管理比较复杂的状态时,使用 useReducer 才是最优解。

useReducer 接受一个形如 (state, action) => newState 的 reducer, 并返回当前的 state与dispatch 方法。

const [state, dispatch] = useReducer(reducer, initialArg, init);

使用例子

interface InitState {
  count: number;
}
type ActionType =
  | {
      type: 'increment';
    }
  | {
      type: 'decrement';
    };

const initCount = { count: 0 };
const counterReducer = (state: InitState, action: ActionType) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
};
const Counter = () => {
  const [countState, dispatch] = useReducer(counterReducer, initCount);
  return (
    <div>
      <p>count: {countState.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+1</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
    </div>
  );
};
export default Counter;

6、useContext + useReducer 代替 Redux

使用例子

在这里插入图片描述

地址: useContext + useReducer 代替 Redux

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

React Hooks 入门下 的相关文章

  • React中的“计算属性”

    React中的 计算属性 相信许多学习过vue的小伙伴对计算属性都不陌生吧 计算属性能帮我们数据进行一些计算操作 计算属性是依赖于data里面的数据的 在vue中只要计算属性依赖的data值发生改变 则计算属性就会调用 那React中也有计
  • react项目按需加载报错 .libraryName is not a valid Plugin property

    babel presets react app plugins import libraryName antd style true 原配置如上会报错 libraryName is not a valid Plugin property g
  • 在react项目中,使用craco插件进行mobx配置解决方案

    在使用react项目中 不可避免的要使用蚂蚁金服出品的ant desgin前端UI组件 ant desgin推荐使用 craco 一个对 create react app 进行自定义配置的社区解决方案 对 create react app
  • 【React】路由(详解)

    目录 单页应用程序 SPA 路由 前端路由 后端路由 路由的基本使用 使用步骤 常用组件说明 BrowserRouter和HashRouter的区别 路由的执行过程 默认路由 精确匹配 Switch的使用 重定向路由 嵌套路由 向路由组件传
  • 在校学生如何白嫖黑群辉虚拟机和内网穿透,实现海量资源的公网访问?(小白专用)

    文章目录 前言 本教程解决的问题是 按照本教程方法操作后 达到的效果是 前排提醒 1 搭建群辉虚拟机 1 1 下载黑群辉文件 vmvare虚拟机安装包 1 2 安装VMware虚拟机 1 3 解压黑 群辉虚拟机文件 1 4 虚拟机初始化 1
  • Ionic3开发教程 - 开发(2)

    Ionic3开发系列教程Ionic3开发教程 环境准备 1 Ionic3开发教程 开发 2 Ionic3开发教程 发布Android版本 3 Ionic3开发教程 发布IOS版本 4 Ionic3开发教程 更新 5 本文中介绍的Ionic3
  • Vite搭建react+ts项目

    创建一个react项目 首先需要打开终端 进行vite的引入 yarn create vite 使用react模板创建项目 yarn create vite react test template react cd react test y
  • 【react】新旧生命周期对比

    componentWillUpdate componentWillReceiveProps componentWillMount 上述这三个生命周期在V18以上的版本中 使用时要加上UNSELF name
  • Ant Design Pro从零到一(认识AntD)

    废话 在我们第一次接触AntD的时候 会遇到两个东西 一个是Ant Design 另一个是Ant Design Pro 他们的官网分别是 Ant Design 一套企业级 UI 设计语言和 React 组件库 Ant Design Pro
  • react和react jsx基础

    本文是个人学习笔记 例子都是来自React Native官网 之前不是做前端的 没有使用过react 要学习react native做混合开发 react 包括react jsx还是得补补 react和react jsx react是一个j
  • React、Vue2.x、Vue3.0的diff算法

    前言 本文章不讲解 vDom 实现 mount 挂载 以及 render 函数 只讨论三种 diff 算法 VNode 类型不考虑 component functional component Fragment Teleport 只考虑 E
  • React 定时刷新接口

    通过 useEffect 在页面加载时调用 getNodeDetailList 列表接口 useEffect gt getNodeDetailList change 然后通过 setInterval 来进行定时刷新 useEffect gt
  • react 父组件调用子组件的方法

    子组件中 const child forwardRef props ref gt useImperativeHandle ref gt 这里面的方法是暴露给父组件的 test console log 我是组件里的test方法 test2 t
  • 值得收藏的UmiJS 教程

    点击上方关注 前端技术江湖 一起学习 天天进步 前言 网上的umi教程是真的少 很多人都只写了一点点 很多水文 所以打算自己写一篇 自己搭建umi 并封装了一下常用的功能 并用到公司实际项目中 umi介绍 Umi 是什么 Umi 中文可发音
  • 如何替换对象的key值

    发生的场景 现在用antd组件库 有些组件想渲染数据的话 我要根据他们官网给的字段名称对应起来才能渲染上去 这个是复选框选中 保存的时候 字段需要按照后台约定的传入code value 1 常规循环遍历 大招来了 哈哈哈 才疏学浅 我觉得是
  • React事件处理及事件流

    React事件处理 React事件处理是通过将事件处理器绑定到组建上处理事件 事件触发的同时更新组建的内部状态 内部状态更新会触发组件的重绘 React 元素的事件处理和 DOM 元素的事件处理很相似 但语法上的略有区别 在React中事件
  • React配置@src根路径

    第一种 直接修改node modules包中的webpack config js文件 找到node modules react scripts config webpack config js文件 修改其中alias中的配置 添加 src
  • reactJS 干货(reactjs 史上最详细的解析干货)

    一 State和 Props state是状态机 应该包括 那些可能被组件的事件处理器改变并触发用户界面更新的数据 譬如需要对用户输入 服务器请求或者时间变化等作出响应 不应该包括 计算所得数据 React组件 在render 里使用pro
  • React中渲染html结构---dangerouslySetInnerHTML

    dangerouslySetInnerHTML 胡子 语法绑定的内容全部作为普通文本渲染 渲染html结构基于 dangerouslySetInnerHTML dangerouslySetInnerHTML 是 React 标签的一个属性
  • 如何提高React组件的渲染效率的?在React中如何避免不必要的render?

    面试官 说说你是如何提高组件的渲染效率的 在React中如何避免不必要的render 一 是什么 react 基于虚拟 DOM 和高效 Diff 算法的完美配合 实现了对 DOM 最小粒度的更新 大多数情况下 React 对 DOM 的渲染

随机推荐

  • SpringBoot3集成RocketMq

    标签 RocketMq5 Dashboard 一 简介 RocketMQ因其架构简单 业务功能丰富 具备极强可扩展性等特点被广泛应用 比如金融业务 互联网 大数据 物联网等领域的业务场景 二 环境部署 1 编译打包 1 下载5 0版本源码包
  • tidefinger(指纹识别)

    1 工具介绍 TideFinger 一个开源的指纹识别小工具 使用了传统和现代检测技术相结合的指纹检测方法 让指纹检测更快捷 准确 是由tide安全团队设计而来 下载地址 https github com TideSec TideFinge
  • Debain 系统U盘安装完全图解

    习惯了使用图形界面的操作 总有一股想要切换到文字界面的Linux的冲动 刚好趁家里的老台式机 没什么用了 就打算用来玩下Linux 在一路安装与使用的过程中 碰到了许多的问题 顺便记录下来 以希望可以帮到需要的人 一 准备工作 160 16
  • muduo日志3

    日志滚动 日志滚动条件 1 文件大小 例如每写满1G换下一个文件 2 时间 每天零点新建一个日志文件 不论前一个文件是否写满 一个典型的日志文件名 logfile test 20130411 115604 popo 7743 log Log
  • 第一天学java

    1 java是什么 java是一门面向对象的编程语言 java是一门准动态编程语言 2编写java的过程 编写 编译 运行 编译会产生class 文件 3配置java运行环境 1安装jdk gt gt 我的是 E environment j
  • DOCKER 相关笔记

    Docker 镜像使用的 rootfs 往往由多个 层 layes 组成 而在使用镜像时 Docker 会把这些增量联合挂载在一个统一的挂载点上 等价于前面例子里的 C 目录 这个挂载点为 var lib docker aufs mnt x
  • SpringBoot工作原理

    SpringBoot工作原理 Spring Boot是由Pivotal团队提供的全新框架 其设计目的是用来简化新Spring应用的初始搭建以及开发过程 该框架使用了特定的方式来进行配置 从而使开发人员不再需要定义样板化的配置 通过这种方式
  • (四)JSP语法详细介绍--脚本元素

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 通过scriptlet可以在jsp中嵌入java代码 可以定义全局变量 方法 类 可以定义局部变量 方法 类 输出一个变量或具体内容 等同于 也可以用以下XML语句代替
  • java中枚举类将属性传值前台(枚举类前台接收问题)

    最近做的这个项目中 用到了大量的枚举类 今天来记录一下我遇到的问题 如果能帮到大家就更好了 1 枚举类如何转为json 在一个类的属性中 这个枚举类属性如何直接使用在接收参数和向数据库传递参数时需要自动转化 这里需要用到两个注解 JSONT
  • linux学习笔记(1)--基础介绍

    目录 linux的初步介绍 1 linux的特点 2 初步介绍 2 1谁挺linux 2 2 linux的历史 linux的初步介绍 1 linux的特点 1 免费的 开源 2 支持多线程 多用户 3 安全性好 4 对内存和文件管理优越 5
  • scala学习-scala中:: , +:, :+, :::, +++的区别

    4种操作符的区别和联系 该方法被称为cons 意为构造 向队列的头部追加数据 创造新的列表 用法为 x list 其中x为加入到头部的元素 无论x是列表与否 它都只将成为新生成列表的第一个元素 也就是说新生成的列表长度为list的长度 1
  • Java Stream API

    之前写过函数表达式介绍过stream的创建和一些基本使用方法 但是后来发现除了简单map filter distinct等API方法 实际上这些可以API组合在一起使用 有时候会有特别的思路 比如最近看的一个写法 for int i 0 i
  • 使用 TensorFlow 和 Flask 部署 Keras 图像分类卷积神经网络模型

    通常需要抽象出您的机器学习模型细节 然后将其与易于使用的 API 端点部署或集成 例如 我们可以提供一个 URL 端点 任何人都可以使用它来发出 POST 请求 他们将获得模型推断的 JSON 响应 而不必担心其技术问题 在本教程中 我们将
  • kafka消费者模式

    一 单线程消费者模式 package nj zb kb23 kafka import org apache kafka clients consumer ConsumerConfig import org apache kafka clie
  • 关闭Windows Defender实时保护,暂时关闭和永久关闭方法

    暂时关闭Windows Defender实时保护 点击开始 设置 更新和安全 Windows安全中心 打开Windows安全中心 点击主页 病毒和威胁防护或管理设置 关闭实时保护 这样就暂时关闭了实时保护 就算不重启也可能某个时候又自动打开
  • 结构化查询语言之 SQL 视图定义(以 MySQL 为例)

    文章目录 1 视图介绍 2 视图定义 3 视图更新 查询使用的数据库文件下载 1 视图介绍 虚关系 并不预先计算并存储关系 而是在使用虚关系时才通过执行查询被计算出来 概念上包含查询的结果 任何不是逻辑模型的一部分 但作为虚关系对用户可见的
  • 中国1949至2019年的gdp图表_成都市1949至2019年,70年来历年GDP数据信息公布

    新中国成立时至此已经整整70年了 在这70年里成都市的经济发展可谓是非常亮眼 最近成都市发布的 成都市统计年鉴2019 受到了不少人的关注 其中最受关注的还是成都历年地区生产总值 也就是我们常说的GDP 具体名单见文末 在 成都统计年鉴20
  • Java中方法的学习

    目录 Java中的方法定义 设计方法的原则 方法的命名规则 代码实现 方法调用 方法的重载 方法学习不知死过多少次 还让我学是吧 你没完了哈 来 来 来 咱们一起来分析 老师 前面的关键字我讲过吧 数据类型还用说嘛 方法的定义格式我说过吧
  • 2023推免夏令营末班车

    南航 清华大学预推免全面开放 目录 曲阜师范大学 活动内容 哈工大预推免 学校 学院 网址 ddl result schedule 河海大学 人工智能与自动化学院 河海大学人工智能与自动化学院2023年全国优秀大学生夏令营活动有关安排的通知
  • React Hooks 入门下

    前面的话 上篇介绍了 useState 和 useEffect 两个钩子函数 这篇将接着介绍其他常用的钩子函数 1 useCallback 作用 该 hooks 返回一个 memoized 回调函数 根据依赖项来决定是否更新函数 其依赖项可