React Hooks的讲解与教程

news2024/12/24 8:21:19

什么是React Hooks

React Hooks 是 React 16.8 新增的一种特性,它可以让你在函数式组件中使用 state、生命周期、context 等 React 特性。Hooks 可以让你将复杂的逻辑拆分成多个独立可重用的函数,并且使得这些函数更加易于理解和测试。

Class组件存在的问题

  1. 复杂度高:类组件的定义和使用更加复杂繁琐。在类组件中,需要继承 React.Component 类,并且还要管理 this 指针、生命周期等,这使得代码变得冗长且难以理解。

  2. 嵌套层数过多:在传统 class 组件编写方式中,为了实现一些复杂逻辑或者 UI 界面,在不同生命周期函数中进行数据更新等操作容易导致代码嵌套层数过多、阅读性差,并且增加额外的复杂度和深度嵌套。

  3. 对功能模块封装比较困难:对于某个特定的功能模块或业务逻辑而言,在类组件内部如果想要将其封装成一个独立的方法或者函数,则往往需要定义私有属性或方法来实现封装。然而这样增加了代码耦合性并降低了可重用性。

  4. 不利于优化:由于 React 生命周期方法是按顺序执行的,因此在 componentDidMount 中做出昂贵计算可能会影响首次渲染时间。即使我们可以使用 shouldComponentUpdate 和 PureComponent 来减少重新渲染次数, 但仍然无法避免当 props 和 state 变化时带来的重新渲染。

React Hooks的优势?

React Hooks 的出现主要是为了解决 React Class组件的缺陷,并且让我们的函数组件也可以拥有类似于类组件的状态管理和副作用处理能力。

  1. 更简单的实现代码复用:在React Hooks出现之前,我们想要实现代码逻辑的复用需要使用到高阶组件、render props 等技术来帮助我们完成,但这样会增加额外的复杂度和深度嵌套,而 Hook 可以更轻松地实现代码逻辑的重用。

  2. 使代码逻辑更清晰:在传统的 class 组件编写方式中,在不同生命周期函数中进行数据更新时容易导致代码难以理解和维护。Hook 可以让你将相关业务逻辑放到同一个函数内部,从而提高可读性并减少了模块之间相互影响带来风险。

  3. 更便于大规模开发与测试:通过使用 Hook ,React 组件变成纯粹声明式渲染效果, 并且对应该渲染结果只由 Props 和 State 决定, 这种特点使得 React 开发者倾向于采取函数式编程(Functional Programming)范例去建立 UI.

  4. 更加灵活和自由 :Hooks 提供了很多新特性例如 state、useEffect、useContext 等,使得函数组件拥有了更多的功能和使用场景,并且可以自由地将这些 Hook 组合在一起以实现各种需求。

Hook的出现,可以解决我们Class组件存在的这些问题,它可以让我们在不编写class的情况下使用state以及其他的React特性。

注意:Hook只能在函数组件中使用,不能在类组件,或者函数组件之外的地方使用

Class组件和Functional组件对比

image.png

State Hook的API

useState

作用

useState,它可以帮助我们在函数式组件中管理状态。

语法

const [state, setState] = useState(initialState);
  • state: 表示当前的状态值。
  • setState: 是一个更新 state 值的函数,调用该函数会重新渲染组件并更新状态。

参数

  • initialState: 表示 state 的初始值。可以是任何的类型,例如数字、字符串、数组或对象等。

返回值

  • 数组:包含两个元素 [state, setState]

使用方法

定义了一个计数器组件,使用了 useState 来初始化 count 状态,并且通过 setCount 函数来更新 count 状态。当用户点击 Increment 按钮时,就会触发 increment 函数并将 count 增加一次。在此过程中,React 自动检测到状态变化,并重新渲染页面以显示最新结果。

import React, { useState } from "react";

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

  function increment() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default Counter;

Effect

作用

useEffect 它可以让我们在函数式组件中执行副作用操作(例如:从服务器获取数据、手动更新 DOM 等)。

语法

useEffect(effect, dependencies);
  • effect:表示要执行的副作用函数。
  • dependencies:表示 useEffect 需要监测变化的依赖项数组。

参数

  • effect: 副作用函数。该函数会在每次渲染时都被调用,并且可以根据需要返回清理函数。如果不需要清理,则应该返回 undefined 或者空函数。
  • dependencies: 可选参数,传入一个数组来指定监测哪些状态/属性变化而触发副作用操作。

返回值

无返回值。

使用方法

import React, { useState, useEffect } from "react";
import axios from "axios";

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    async function fetchData() {
      const result = await axios("https://jsonplaceholder.typicode.com/posts");
      setData(result.data);
    }
    fetchData();
  }, []);

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

export default App;

在上面的代码示例中我们定义了一个简单的应用程序,我们使用了 useState 来初始化 data 状态,并且在 useEffect 中使用 axios 库从服务器获取数据。当数据加载完成时,我们使用 setData 函数更新 data 状态,并且通过 map 方法将每个帖子的标题显示为列表项。在此过程中,React 自动检测到状态变化,并重新渲染页面以显示最新结果。

useContext

作用

useContext 它可以让我们在组件树中跨层级传递数据,避免了繁琐的 props 传递。

用法

首先,我们需要创建一个 Context 对象来存储要共享的数据:

// MyContext.js

import React from "react";

const MyContext = React.createContext();

export default MyContext;

然后,在父组件中设置共享数据,并将其作为 value 属性传递给 Context.Provider 组件:

// App.js

import React, { useState } from "react";
import MyContext from "./MyContext";
import ChildComponent from "./ChildComponent";

function App() {
  const [name, setName] = useState("John");

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <MyContext.Provider value={name}>
        <ChildComponent />
      </MyContext.Provider>
    </div>
  );
}

export default App;

这里我们使用了 useState 来定义状态变量 name 和用于更新该变量的函数 setName。然后将其作为值传递给 Context。

接下来,在子组件中使用 useContext Hook 来获取共享数据并进行展示:

// ChildComponent.js

import React, { useContext } from "react";
import MyContexct from "./MyContexct";

function ChildComponent() {
  const name = useContext(MyContexct);

  return (
    <div>
      <h2>Child Component</h2>
      <p>{name}, how are you doing?</p>
    </div>
  );
}

export default ChildComponent;

如上述代码所示,我们可以通过 useContext Hook 来获取 Context 中的共享数据,并将其命名为 name。然后在组件中展示该数据。

最终,当我们运行这个应用程序时,它将显示一个包含 "Hello, John!""John, how are you doing?" 的页面。

注意:到子组件无需通过 props 获取父组件的状态变量值即可访问该值。

useReducer

作用

useReducer 它可以帮助我们使用 reducer 函数管理复杂的组件状态。

用法

首先,我们需要定义一个 reducer 函数来管理组件状态。该函数接收两个参数:当前状态(state)和更新该状态所需的操作(action)。然后根据 action 的类型进行相应的处理并返回新的 state。

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { count: state.count + 1 };
    case "DECREMENT":
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

在上面的代码中,我们创建了一个名为 reducer 的函数,并根据传入的不同类型执行相应操作来更新组件状态。

接下来,在父组件中使用 useReducer Hook 来初始化和获取共享数据以及 dispatch 方法:

import React, { useReducer } from "react";
import reducer from "./reducer";

function App() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  function increment() {
    dispatch({ type: "INCREMENT" });
  }

  function decrement() {
    dispatch({ type: "DECREMENT" });
  }

  return (
    <div>
      <h2>Count:{state.count}</h2>
        <button onClick={increment}>Increment</button>
        <button onClick={decrement}>Decrement</button>
     </div>
   );
}

export default App;

如上述代码所示,通过调用 useReducer Hook,我们可以获取到当前的状态(state)和 dispatch 方法。其中第一个参数是 reducer 函数,第二个参数是初始状态值。在这里我们使用了 { count: 0 } 作为初始 state 值。

然后在父组件中定义两个函数:increment 和 decrement,它们分别调用 dispatch({ type: "INCREMENT" })dispatch({ type: "DECREMENT" }) 来更新共享数据。

最终,在返回的 JSX 中展示当前计数器的值并添加增加/减少按钮来触发对应操作即可。

useCallback

作用

useCallback 它可以帮助我们优化性能并避免不必要的重新渲染。

用法

首先,我们需要理解什么是函数记忆。在 JavaScript 中,如果两个相同参数值之间存在对应关系,则称这些函数有“记忆”。因此,在进行计算时使用已经计算过的结果而不是再次运行该函数以提高效率。

接下来,请看如下代码示例:

import React, { useState } from "react";

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

  function handleClick() {
    console.log("Clicked!");
    setCount(count + 1);
  }

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={handleClick}>Increment</button>
    </div>
   );
}

export default App;

上面的代码中展示了一个简单的计数器组件,并且定义了一个名为 handleClick 的事件处理程序方法。当点击按钮时,会触发该方法并更新内部状态以反映当前值。

然而,在每次渲染组件时都会创建新的 handleClick 方法实例可能会导致一定程度上的性能问题。这就是为什么我们需要使用 useCallback 来进行优化。

使用 useCallback 来改写后:

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

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

  const handleClick = useCallback(() => {
    console.log("Clicked!");
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={handleClick}>Increment</button>
    </div>
   );
}

export default App;

在上面的代码中,我们使用了 useCallback 来缓存 handleClick 方法并根据需要更新它。这样,在组件重新渲染时就不会创建新的实例,从而避免了性能问题。

注意:第二个参数 [count] 是依赖项数组。如果该值发生变化,则会触发回调函数的重新计算和更新处理程序方法。

useMemo

作用

useMemo ,它可以帮助我们优化性能并避免不必要的重新计算。

用法

首先,我们需要理解什么是记忆。在 JavaScript 中,如果两个相同参数值之间存在对应关系,则称这些函数有“记忆”。因此,在进行计算时使用已经计算过的结果而不是再次运行该函数以提高效率。

import React, { useState } from "react";

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

  function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
  }

  const result = fibonacci(count);

  return (
    <div>
      <h2>Result: {result}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
   );
}

export default App;

上面的代码中展示了一个简单的斐波那契数列组件,并且定义了一个名为 fibonacci 的递归方法。每当用户点击按钮时都会调用该方法以更新内部状态以反映当前值。

然而,在每次渲染组件时都会重新计算斐波那契数列可能会导致一定程度上的性能问题。这就是为什么我们需要使用 useMemo 来进行优化。

改写后如下所示:

import React, { useState, useMemo } from "react";

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

  function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
  }

  const result = useMemo(() => fibonacci(count), [count]);

  return (
    <div>
      <h2>Result: {result}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
   );
}

export default App;

在上面的代码中,我们使用了 useMemo 来缓存斐波那契数列计算结果并根据需要更新它。这样,在组件重新渲染时就不会重新计算该值,从而避免了性能问题。

注意:第二个参数 [count] 是依赖项数组。如果该值发生变化,则会触发回调函数的重新计算和更新处理程序方法。

useRef

作用

useRef 它可以帮助我们在函数组件中保存可变值,并且不会导致组件重新渲染。

用法

import React, { useState } from "react";

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

  function handleAlertClick() {
    setTimeout(() => {
      alert(`You clicked ${count} times`);
    }, 3000);
  }

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={handleAlertClick}>Show Alert</button>
    </div>
   );
}

export default App;

上面的代码中展示了一个简单的计数器组件,并且定义了一个名为 handleAlertClick 的事件处理程序方法。当用户点击“Show Alert”按钮时,该方法会等待三秒钟然后显示警告框以反映当前值。

然而,在该三秒钟内如果用户再次点击“Increment”按钮,则计数器将增加并且警告框仍将显示旧的值。这就是因为每次重新渲染组件时都创建了新的 count 变量实例。

改写后如下所示:

import React, { useState, useRef } from "react";

function App() {
  const [count, setCount] = useState(0);
  
  const countRef = useRef(count);

  function handleAlertClick() {
    setTimeout(() => {
      alert(`You clicked ${countRef.current} times`);
    }, 3000);
  }

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={handleAlertClick}>Show Alert</button>
    </div>
   );
}

export default App;

在上面的代码中,我们使用了 useRef 来保存可变值,并且不会导致组件重新渲染。这样,在每次重新渲染时都可以访问到最新的计数器值。

注意:需要将初始值传递给 useRef,并通过 .current 属性来读写该值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/702200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java的模块(module)

简单介绍 Java的模块&#xff08;module&#xff09;是Java SE 9支持的组件。一个模块可以包含多个包。通过模块系统&#xff0c;更加容易开发和维护大型的系统。引入模块的目标&#xff1a; 提供一种可靠的配置机制。用模块机制替换脆弱的、容易出错的类路径配置机制。模块作…

物化视图功能验证

物化视图(Materialized View)和视图(View)类似&#xff0c;也是一个视图名字对应一个SQL查询查询语句。不同之处在于&#xff1a;物化视图定义时使用了额外的关键字materialized&#xff0c; 它把结果集保存在起来&#xff0c;查询的时候直接读取保存的结果集&#xff0c;而不必…

力扣单调栈专题 739. 每日温度 496.下一个更大元素I 503.下一个更大元素II 42. 接雨水 84.柱状图中最大的矩形 步骤及C++实现 总结

文章目录 739. 每日温度496.下一个更大元素 I503.下一个更大元素II方法一 数组拼接方法二 模拟遍历两边nums 42. 接雨水双指针 暴力法双指针优化单调栈 84.柱状图中最大的矩形暴力解法双指针解法单调栈 总结 739. 每日温度 暴力解法&#xff0c;双层for循环&#xff0c;时间复杂…

「完美解决」concrt140.dll丢失怎么恢复(解决方案)

我们平时在打开 Adobe 应用程序、Halo、Forza Horizon 5 等时&#xff0c;可能会遇到找不到 concrt140.dll。因此&#xff0c;这不是特定于某个应用程序的问题。如果没有安装正确的 DLL&#xff0c;应用程序将无法正常工作&#xff0c;因为它们的代码依赖于这些库中编写的代码。…

C++入门笔记--【精简完备】【快速入手】

目录 ​​​​​​​1 C初识 1.1 第一个C程序 1.1.1 创建项目 1.1.2 创建文件 1.1.3 编写代码 1.1.4 运行程序 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1.6 标识符命名规则 2 数据类型 2.1 整型 2.2 sizeof关键字 2.3 实型&#xff08;浮点型&#xff09; 2.4 字…

基于STM32单片机的大棚温度测控系统

目录 基于STM32单片机的大棚温度测控系统一、原理图二、部分代码三、视频演示 基于STM32单片机的大棚温度测控系统 功能&#xff1a; 1.通过LCD屏幕显示温湿度值和上限阈值 2.通过按键完成阈值加减功能 3.通过DHT11完成温湿度采集 4.若实时数据不满足条件&#xff0c;则进行降…

linux curl命令使用教程(curl指令)

文章目录 curl指令介绍curl的历史curl指令的安装Ubuntu/DebianCentOS/RHELFedoramacOSWindows curl使用方法示例使用curl命令注意事项 curl指令介绍 curl是一个用于发送HTTP请求的命令行工具&#xff0c;它支持多种协议&#xff0c;包括HTTP、HTTPS、FTP、SMTP等。curl可以用来…

人工智能:揭示未来科技所带来的革命性变革

目录 引言&#xff1a; 一、人工智能的定义与发展历程&#xff1a; 二、人工智能的应用领域&#xff1a; 三、人工智能对未来的影响&#xff1a; 结论&#xff1a; 引言&#xff1a; 在当今科技快速发展的时代&#xff0c;人工智能&#xff08;Artificial Intelligence&am…

开展全媒体营销的具体步骤和策略

随着移动互联网的发展和智能手机的普及&#xff0c;网络营销的主战场从PC端向移动端转移&#xff0c;新型的全媒体营销就是在如此的背景下兴起且被广泛应用。全媒体营销强调的是通过多个平台和渠道传播信息&#xff0c;与目标受众进行全方位、多角度的沟通。 企业如何高效开展全…

1σ Random Jitter of SSB for TIE Period

1σ Random Jitter of SSB for TIE & Period &#xff0c;该章节厘清了jitter基本概念&#xff0c;并给出了对应的表达式。

FineReport学习4

通过按钮控件来实现插入删除行 通过sql公式实现填报联动 设置插入行策略 通过sql实现控件联动

数据结构---------(2)排序

排序 1.选择排序 从待排序的数据中选择最小的元素&#xff0c;将其放在已排序的序列末尾&#xff0c;然后在剩余的数据中再选择最小的元素&#xff0c;放在已排序序列的末尾&#xff0c;以此类推&#xff0c;直到所有的数据都排好序为止。 public static void main(String[…

大数据分析案例-基于KMeans聚类算法对客户进行分群分析

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【GESP】2023年03月图形化二级 -- 绘制图形

文章目录 绘制图形1. 准备工作2. 功能实现3. 设计思路与实现&#xff08;1&#xff09;角色、舞台背景设置a. 角色设置b. 舞台背景设置 &#xff08;2&#xff09;脚本编写a. 角色&#xff1a;小猫 4. 评分标准 绘制图形 1. 准备工作 &#xff08;1) 隐藏默认角色小猫。 2. 功…

【Redis入门】 —— 关于Redis的一点儿知识

&#x1f4a7; 【 R e d i s 入门】——关于 R e d i s 的一点儿知识 \color{#FF1493}{【Redis入门】 —— 关于Redis的一点儿知识} 【Redis入门】——关于Redis的一点儿知识&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页…

2023年华中杯数学建模B题小学数学应用题相似性度量及难度评估解题全过程文档及程序

2023年华中杯数学建模 B题 小学数学应用题相似性度量及难度评估 原题再现 某 MOOC 在线教育平台希望能够进行个性化教学&#xff0c;实现用户自主学习。在用户学习时&#xff0c;系统从题库中随机抽取若干道与例题同步的随堂测试题&#xff0c;记录、分析学生的学习和答题信息…

深入Vite:再谈ESM的高阶特性

谈到前端模块化的发展历史&#xff0c;就一定不会漏掉ESM&#xff0c;除此之外&#xff0c;还有大家熟知的CommonJS、AMD、CMD以及ES6等。目前&#xff0c; ESM 已经逐步得到各大浏览器厂商以及 Node.js 的原生支持&#xff0c;正在成为主流前端模块化方案。 而 Vite 本身就是…

chatgpt赋能python:Python除零错误:原因,解决办法和实践建议

Python 除零错误&#xff1a;原因&#xff0c;解决办法和实践建议 介绍 Python 作为一门广泛使用的高级编程语言&#xff0c;它的强大之处就体现在它的简洁性、可读性和易用性上。但是在实践中&#xff0c;有时候我们会遇到一些让我们不得不头痛的问题&#xff0c;其中之一就…

(0017) H5-vue创建项目vue init webpack

1、初始化项目 1、vue 命令讲解 vue list &#xff1a;查看可以基于那些模板创建vue应用vue init <template-name> <project-name>init&#xff1a;表示要用vue-cli来初始化项目 <template-name>&#xff1a;表示模板名称&#xff0c;vue-cli官方提供的5种…

【LeetCode】218. 天际线问题

218. 天际线问题&#xff08;困难&#xff09; 思路 题意转化 完整思路分析 multiset的使用 multiset 是关联容器的一种&#xff0c;是排序好的集合&#xff08;元素默认升序&#xff09;&#xff0c;并且允许有相同的元素。 不能直接修改 multiset 容器中元素的值。因…