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

React的基础API介绍(二)

目录

    • useState
      • useState 的基本原理
        • 1. 状态在函数组件中的引入
        • 2. useState 的工作机制
        • 3. Hook 状态与组件渲染
      • useState 的使用方法
        • 1. 基本用法
        • 2. 多个状态变量
        • 3. 更新状态
      • 注意事项与最佳实践
        • 1. 状态更新可能是异步的
        • 2. 不要直接修改状态
        • 3. 更新对象或数组状态
        • 4. 避免闭包陷阱
      • 进阶用法
        • 1. 自定义 Hook
        • 2. 状态与副作用的结合
      • useState 与 useReducer 的对比
      • 为什么 Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用?
        • 1. React 如何跟踪 Hooks
        • 2. 为什么需要保持 Hooks 的调用顺序一致
        • 3. 示例解释
        • 4. 如何在条件情况下使用 Hooks

useState

useState 是 React Hooks 中最基本也是最常用的一个 Hook,用于在函数组件中添加状态管理功能。它使得函数组件能够像类组件一样拥有内部状态,而无需编写类组件的样板代码。

useState 的基本原理

1. 状态在函数组件中的引入

在传统的 React 类组件中,状态(state)是通过 this.state 和 this.setState 来管理的。函数组件最初是无状态的,仅根据传入的 props 进行渲染。

useState 的引入改变了这一点,它允许在函数组件中引入状态。每次调用useState,都创建了一个状态变量和更新该状态的函数。

2. useState 的工作机制
  • 状态的持久化:useState 通过闭包的方式,在组件的多次渲染之间保持状态的持久化。
  • 状态更新:当调用状态更新函数时,React 会将新的状态值入队,并触发组件重新渲染。在下一次渲染时,useState 会返回更新后的状态值。
  • Hook 的调用顺序:React 依赖于 Hook 调用的顺序来正确地管理状态。因此,Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用。
3. Hook 状态与组件渲染
  • 组件重新渲染:当状态更新时,组件会重新渲染。函数组件会重新执行,生成新的 JSX。
  • 状态的稳定性:虽然组件函数会重新执行,但 useState 返回的状态在多次渲染之间是稳定的。React 内部通过一个链表结构来跟踪每个 Hook 的状态。

useState 的使用方法

1. 基本用法
import React, { useState } from 'react';function Counter() {// 声明一个新的状态变量 "count",初始值为 0const [count, setCount] = useState(0);return (<div><p>当前计数:{count}</p><button onClick={() => setCount(count + 1)}>增加</button></div>);
}export default Counter;

解释:

初始化状态:useState(0) 初始化了一个状态变量 count,初始值为 0。
状态变量:count 是当前的状态值。
更新函数:setCount 是更新状态的函数,调用它可以更新 count 的值并触发组件重新渲染。

2. 多个状态变量

可以在同一个组件中多次使用 useState,每个状态变量都是独立的。

function UserProfile() {const [name, setName] = useState('李四');const [age, setAge] = useState(30);return (<div><p>姓名:{name}</p><p>年龄:{age}</p><button onClick={() => setAge(age + 1)}>增加年龄</button></div>);
}
3. 更新状态

直接赋值更新:

setCount(newValue);

基于前一个状态更新(函数式更新):
当新的状态需要依赖之前的状态时,使用函数式更新。

setCount(prevCount => prevCount + 1);

好处: 避免了状态更新的异步性带来的问题,确保基于最新的状态进行计算。

注意事项与最佳实践

1. 状态更新可能是异步的

不可立即获取更新后的状态: 调用 setState 后,状态并不会立即更新,新的状态会在下一次渲染时生效。

避免依赖立即更新的状态:

// 错误示例
setCount(count + 1);
console.log(count); // 仍然是旧的 count 值// 正确示例
setCount(prevCount => {const newCount = prevCount + 1;console.log(newCount); // 新的 count 值return newCount;
});
2. 不要直接修改状态

状态应该被视为不可变的。不要直接修改状态变量,而是创建新的状态值。

// 错误示例
state.value = newValue;// 正确示例
setState({ ...state, value: newValue });
3. 更新对象或数组状态

useState 不会自动合并更新对象或数组,需要手动合并。

const [user, setUser] = useState({ name: '张三', age: 25 });// 更新年龄
setUser(prevUser => ({ ...prevUser, age: prevUser.age + 1 }));
4. 避免闭包陷阱

在异步操作中,可能会捕获到旧的状态值。使用函数式更新可以避免这个问题。

useEffect(() => {const timer = setTimeout(() => {// 使用函数式更新,确保获取最新的状态值setCount(prevCount => prevCount + 1);}, 1000);return () => clearTimeout(timer);
}, []);

进阶用法

1. 自定义 Hook

可以将状态逻辑封装到自定义 Hook 中,以便在多个组件之间复用。

function useCounter(initialValue = 0) {const [count, setCount] = useState(initialValue);const increment = () => setCount(prev => prev + 1);const decrement = () => setCount(prev => prev - 1);return { count, increment, decrement };
}// 在组件中使用
function Counter() {const { count, increment, decrement } = useCounter(10);return (<div><p>计数:{count}</p><button onClick={increment}>增加</button><button onClick={decrement}>减少</button></div>);
}
2. 状态与副作用的结合

useState 通常与 useEffect 一起使用,响应状态的变化执行副作用操作。

function DataFetcher() {const [data, setData] = useState(null);useEffect(() => {let isMounted = true;fetchData().then(response => {if (isMounted) setData(response);});return () => {isMounted = false;};}, []);return <div>{data ? data.content : '加载中...'}</div>;
}

useState 与 useReducer 的对比

当状态逻辑较为复杂,或者状态更新依赖于前一个状态时,useReducer 可能是更好的选择。

function reducer(state, action) {switch (action.type) {case 'increment':return { ...state, count: state.count + 1 };case 'decrement':return { ...state, count: state.count - 1 };default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, { count: 0 });return (<div><p>计数:{state.count}</p><button onClick={() => dispatch({ type: 'increment' })}>增加</button><button onClick={() => dispatch({ type: 'decrement' })}>减少</button></div>);
}

useReducer 的优势:

更清晰的状态管理:适用于复杂状态逻辑,状态更新逻辑集中在 reducer 函数中。

为什么 Hook 应该始终在组件顶层调用,不能在条件、循环或嵌套函数中使用?

1. React 如何跟踪 Hooks
  • Hooks 的调用顺序:React 通过组件中 Hooks 的调用顺序来跟踪每个 Hook 对应的状态。这意味着每次渲染时,Hooks 必须按照相同的顺序被调用。

  • Hook 链表:在内部,React 维护了一个 Hook 链表,记录了每个 Hook 在组件中的位置。当组件重新渲染时,React 依赖于这个调用顺序来正确地匹配当前的 Hook 与之前的状态。

2. 为什么需要保持 Hooks 的调用顺序一致
  • 状态与 Hook 位置关联:由于状态是与 Hook 调用的位置(即调用顺序)相关联的,如果 Hooks 的调用顺序发生变化,React 就无法正确地为每个 Hook 提供对应的状态。

  • 避免状态错位:如果在条件、循环或嵌套函数中调用 Hooks,可能会导致 Hooks 的调用数量或顺序在不同的渲染中发生变化,导致状态错位。

3. 示例解释
  • 错误示例:在条件语句中使用 Hook
function MyComponent({ show }) {if (show) {useState(0);}// 其他代码
}

问题:

当 show 为 true 时,useState 被调用。
当 show 为 false 时,useState 未被调用。
这导致在不同的渲染中,Hooks 的调用数量和顺序发生了变化。

后果:

React 无法正确地匹配 Hook 与之前的状态。
可能会导致状态错位、错误的状态值,甚至引发难以调试的错误。

正确的做法

function MyComponent({ show }) {const [state, setState] = useState(0);// 根据条件渲染内容if (show) {// 使用 state}// 其他代码
}

始终在顶层调用 useState,确保每次渲染时 Hooks 的调用顺序一致。

  • 错误示例:在循环中使用 Hook
function MyComponent({ items }) {items.forEach(item => {useEffect(() => {// 对每个 item 进行副作用操作}, [item]);});// 其他代码
}

问题:

items 的数量可能会变化,导致 useEffect 的调用次数不一致。
Hooks 的调用顺序和数量在不同的渲染中不一致。

后果
React 无法正确地管理 Hooks,导致状态错位。

正确的做法

function MyComponent({ items }) {// 使用一个 Hook 处理所有 itemsuseEffect(() => {items.forEach(item => {// 对每个 item 进行副作用操作});}, [items]);// 其他代码
}

将 Hooks 调用放在顶层,不受循环或条件的影响。

4. 如何在条件情况下使用 Hooks

虽然不能在条件语句中调用 Hooks,但可以通过在 Hooks 内部处理条件逻辑来实现需求。

示例:

function MyComponent({ show }) {const [data, setData] = useState(null);useEffect(() => {if (show) {// 执行副作用fetchData().then(result => setData(result));}}, [show]);// 其他代码
}

解释:useEffect 始终被调用,但在内部根据 show 的值决定是否执行副作用操作。

相关文章:

React的基础API介绍(二)

目录 useStateuseState 的基本原理1. 状态在函数组件中的引入2. useState 的工作机制3. Hook 状态与组件渲染 useState 的使用方法1. 基本用法2. 多个状态变量3. 更新状态 注意事项与最佳实践1. 状态更新可能是异步的2. 不要直接修改状态3. 更新对象或数组状态4. 避免闭包陷阱 …...

远程开发测试必看:如何在群晖NAS上运行网页版Ubuntu

文章目录 前言1. 下载Docker-Webtop镜像2. 运行Docker-Webtop镜像3. 本地访问网页版Linux系统4. 群晖NAS安装Cpolar工具5. 配置异地访问Linux系统6. 异地远程访问Linux系统7. 固定异地访问的公网地址 前言 本文将详细讲解如何在群晖NAS上部署docker-webtop&#xff0c;并利用c…...

JAVA题目笔记(十五)经典算法题

一、按要求排序 要求&#xff1a;定义数组并存储一些女朋友对象&#xff0c;利用Arrays中的sort方法进行排序 属性包括&#xff1a;姓名&#xff0c;年龄&#xff0c;身高 按照年龄大小进行排序&#xff0c;年龄一样按照身高排序&#xff0c;身高一样按照姓名字母进行排序。…...

「Mac玩转仓颉内测版8」入门篇8 - Cangjie函数与方法

本篇介绍Cangjie编程语言中的函数与方法&#xff0c;帮助理解如何通过函数封装重复操作&#xff0c;提升代码的复用性和可维护性。 关键词 Cangjie函数方法定义参数传递返回值模块化与复用性 一、什么是函数&#xff1f; 函数是一个代码块&#xff0c;用于接收参数、执行操作…...

2024最新版JavaScript逆向爬虫教程-------基础篇之Proxy与Reflect详解

目录 一、监听对象的操作二、Proxy基本使用2.1 创建空代理2.2 定义捕获器2.2.1 Proxy的set和get捕获器2.2.2 Proxy(handler)的13个捕获器 三、Reflect的作用3.1 Reflect的使用3.2 Reflect其余方法(9个)3.3 Proxy与Reflect中的receiver参数3.4 Reflect中的construct方法 ECMAScr…...

代码修改材质参数

1、 如何得到对象使用的材质 获取到对象的渲染器Renderer Mesh Renderer和Skinned Mesh Renderer都继承Renderer&#xff0c;可以用里式替换原则父类获取、装载子类对象 通过渲染器获取到对应材质 可以利用渲染器中的material或者sharedMaterial来获取物体的材质&#xff0…...

[C++11] 包装器 : function 与 bind 的原理及使用

文章目录 functionstd::function 的基本语法使用 std::function 包装不同的可调用对象function包装普通成员函数为什么要传入 this 指针参数&#xff1f;传入对象指针与传入对象实例的区别 例题 &#xff1a;150. 逆波兰表达式求值 - ⼒扣&#xff08;LeetCode&#xff09; bin…...

java项目-jenkins任务的创建和执行

参考内容: jenkins的安装部署以及全局配置 1.编译任务的general 2.源码管理 3.构建里编译打包然后copy复制jar包到运行服务器的路径 clean install -DskipTests -Pdev 中的-Pdev这个参数用于激活 Maven 项目中的特定构建配置&#xff08;Profile&#xff09; 在 pom.xml 文件…...

单片机中的BootLoader(重要的概念讲解)

文章目录 一、链接地址和执行地址1. 链接地址(Load Address)2. 执行地址(Execution Address)链接地址与执行地址的关系实际工作流程总结二、相对跳转和绝对跳转1. 相对跳转(Relative Jump)2. 绝对跳转(Absolute Jump)3. `BX` 和 `BL` 指令总结三、散列文件1. 散列文件的…...

【数据分享】中国食品工业年鉴(1984-2023) PDF

数据介绍 一、《中国食品工业年鉴》(以下简称《年鉴》)是一部全面反映上一年度全国食品工业发展情况纪年性、资料性、权威大型年刊。《年鉴(2023)》系统收录了全国食品行业各专业和 31个省(自治区、直辖市)2022年食品工业经济运行情况的综述&#xff0c;《年鉴》是由中国食品工…...

优选算法 - 1 ( 双指针 移动窗口 8000 字详解 )

一&#xff1a;双指针 1.1 移动零 题目链接&#xff1a;283.移动零 class Solution {public void moveZeroes(int[] nums) {for(int cur 0, dest -1 ; cur < nums.length ; cur){if(nums[cur] 0){}else{dest; // dest 先向后移动⼀位int tmp nums[cur];nums[cur] num…...

FairyGUI和Unity联动(入门篇)

一、FairyGUI编辑器中 1.新建按钮、新建组件 编辑器中界面简易设计如下 2.文件-发布设置-发布路径&#xff1a;自己unity项目Resources所在的路径 二、Unity 使用代码展示UI using FairyGUI; using System.Collections; using System.Collections.Generic; using UnityEngi…...

Go:文件输入输出以及json解析

文章目录 读取用户的输入文件读写读文件写文件 文件拷贝io包中接口的概念JSON 数据格式编码解码任意的数据&#xff1a; 读取用户的输入 从键盘和标准输入 os.Stdin 读取输入&#xff0c;最简单的办法是使用 fmt 包提供的 Scan… 和 Sscan… 开头的函数 看如下的程序 func t…...

编写红绿起爆线指标(附带源码下载)

编写需求&#xff1a; 想问问有没有能标注行情起爆点的指标。 效果展示&#xff1a; 红线上&#xff0c;出现绿柱转红柱做多。 蓝线下&#xff0c;出现红柱转绿柱做空。 源码展示&#xff08;部分源码&#xff0c;完整源码需下载源码文件&#xff09;&#xff1a; IsMainIn…...

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能&#xff0c;相比于继承更加灵活 2、类图 Component(VisualComponent)&#xff1a;定义一个对象接口&#xff0c;可以给这些对象动态地添加职责。ConcreteComponent(TextView)&#xff1a;定义一个对象&#xff0c;可以给这个对象添加一…...

Android11 修改系统语言

1.定义一个view <RelativeLayoutandroid:id"id/rlChooseLanguage"style"style/SettingAboutItem"><TextViewstyle"style/SettingAboutItemTextView"android:text"string/choose_language" /><ImageView style"st…...

vue3 查看word pdf excel文件

也是在网上找的基础上修改的 可以直接使用 npm install vue-office/docx npm install vue-office/excel npm install vue-office/pdf​<template><divclass"Office-Preview"v-loading"loading"element-loading-text"文件加载中...">…...

java八股-垃圾回收机制-垃圾回收算法,分代回收,垃圾回收器

文章目录 垃圾回收算法引用计数法可达性分析算法 jvm垃圾回收算法标记清除算法标记整理算法复制算法本章总结 JVM中的分代回收本章总结 JVM有哪些垃圾回收器&#xff1f;1.串行垃圾收集器2.并行垃圾收集器3.CMS&#xff08;并发&#xff09;垃圾收集器本章小结 详细聊一下G1垃圾…...

iSCSI 和FC的概述

一、技术基础与架构 iSCSI 技术基础&#xff1a;iSCSI是基于TCP/IP协议的存储网络协议&#xff0c;它实现了在IP网络上运行SCSI协议。架构&#xff1a;iSCSI协议栈包括SCSI层、iSCSI层、TCP/IP层等&#xff0c;通过标准的以太网技术实现存储数据的传输。 FC 技术基础&#xff1…...

一文了解Android中的AudioFlinger

AudioFlinger 是 Android 音频框架中的一个核心组件&#xff0c;负责管理音频流的混合和输出。它是 Android 音频系统服务的一部分&#xff0c;作为音频框架和硬件之间的桥梁&#xff0c;接收应用程序的音频请求、进行混音处理&#xff0c;并最终通过音频硬件输出声音。 ![在这…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)

UniApp 集成腾讯云 IM 富媒体消息全攻略&#xff08;地理位置/文件&#xff09; 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型&#xff0c;核心实现方式&#xff1a; 标准消息类型&#xff1a;直接使用 SDK 内置类型&#xff08;文件、图片等&#xff09;自…...