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 的值决定是否执行副作用操作。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

acwing算法基础02一高精度,前缀和,差分

#include <iostream> #include <vector> using namespace std;const int N 1e6 10; //模板 CABvector<int> add(vector<int> &A,vector <int> &B) {vector<int> C;int t 0; // 用来保存每位的和&#xff08;包括进位&#xff…

WebAssembly在现代Web开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 WebAssembly在现代Web开发中的应用 WebAssembly在现代Web开发中的应用 WebAssembly在现代Web开发中的应用 引言 WebAssembly 概述…

06.VSCODE:备战大项目,CMake专项配置

娇小灵活的简捷配置不过是年轻人谈情说爱的玩具&#xff0c;帝国大厦的构建&#xff0c;终归要交给CMake去母仪天下。一个没有使用 CMake 的 C 项目&#xff0c;就像未来世界里的一台相声表演&#xff0c;有了德纲却无谦&#xff0c;观众笑着遗憾。—— 语出《双城记》作者&…

从社交媒体到元宇宙:Facebook未来发展新方向

Facebook&#xff0c;作为全球最大的社交媒体平台之一&#xff0c;已经从最初的简单互动工具发展成为一个跨越多个领域的科技巨头。无论是连接人与人之间的社交纽带&#xff0c;还是利用大数据、人工智能等技术为用户提供个性化的体验&#xff0c;Facebook一直引领着社交网络的…

【go从零单排】JSON序列化和反序列化

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;处理 JSON 数据主要依赖于 encoding/json 包。这个包提…

vue2.7.14 + vant + vue cli脚手架转vite启动运行问题记录

文章目录 前言方案一&#xff08;借用插件转换&#xff09;启动命令&#xff0c;转换方案一转换遇到的问题 方案二&#xff08;手动调整&#xff09;方案两者对比小结 前言 vue cli 脚手架转成vite启动 简单说说这个项目的一些底层基本结构哈&#xff0c;以及写这篇博客的目的…

Linux 常用操作指令大揭秘(下)

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; &#x1f6a9;用通俗易懂且不失专业性的文字&#xff0c;讲解计算机领域那些看似枯燥的知识点&#x1f6a9; 目录 &#x1f4af;…

HBase使用create创建表时报错ERROR: KeeperErrorCode = NoNode for /hbase/master

场景模拟 1. 正常情况 模拟ERROR: KeeperErrorCode NoNode for /hbase/master错误场景。 正常情况下创建hbase表如下图所示。 2. 删除hbase集群的zk节点 进入zookeeper客户端。 zkCli.sh删除hbase的zk节点。 deleteall /hbase退出zookeeper客户端。 quit3. 重启hbase集…

【设计模式】行为型模式(二):策略模式、命令模式

行为型模式&#xff08;二&#xff09;&#xff1a;策略模式、命令模式 3.策略模式&#xff08;Strategy&#xff09;3.1 示例3.1.1 定义策略接口3.1.2 实现具体策略3.1.3 定义上下文类3.1.4 客户端代码3.1.5 输出结果 3.2 总结3.2.1 优点3.2.2 缺点 4.命令模式&#xff08;Com…

java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程

文章目录 PC Register堆虚拟机栈方法区(Metaspace元空间双亲委派机制类加载器 类装载的执行过程 PC Register 程序计数器&#xff08;Program Counter Register&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;它在 JVM 的内存模型中扮演着非常…

Python →爬虫实践

爬取研究中心的书目 现在&#xff0c;想要把如下网站中的书目信息爬取出来。 案例一 耶鲁 Publications | Yale Law School 分析网页&#xff0c;如下图所示&#xff0c;需要爬取的页面&#xff0c;标签信息是“<p>”&#xff0c;所以用 itemssoup.find_all("p&…

【Linux】-学习笔记03

第十一章-管理Linux软件包和进程 1.源码下载安装软件 1.1概念 源码文件&#xff1a;程序编写者使用C或C等语言编写的原始代码文本文件 源码文件使用.tar.gz或.tar.bz2打包成压缩文件 1.2特点 源码包可移植性好&#xff0c;与待安装软件的工作环境依赖性不大 由于有编译过程…

排序算法 - 冒泡

文章目录 1. 冒泡排序1.1 简介1.2 基本步骤&#xff1a;1.3 示例代码&#xff08;C&#xff09;1.4 复杂度分析1.5 动画展示 1. 冒泡排序 1.1 简介 冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;其基本思想是通过相邻元素的比较和交换&#…

前端请求后端php接口跨域 cors问题

只需要后端在网站的入口文件 一般都是 index.php 加上 这几行代码就可以了 具体的参数可以根据需要去修改 header("Access-Control-Allow-Origin: *"); header(Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS); header(Access-Control-Allow-Heade…

Django基础用法+Demo演示

Django快速上手 参考: Django快速上手 再写几个页面 编辑demo1/urls.py, 添加URL和视图函数映射 urlpatterns [path(index/, views.index),path(user/list/, views.user_list),path(user/add/, views.user_add), ]编辑app01/views.py&#xff0c;添加几个函数 from djang…

数据集标注txt文件读取小工具

最近在看遥感图像目标检测相关的yolo10&#xff0c;自己在网上下载了数据集跑模型&#xff0c;但是跑出来的结果与数据集出处的论文介绍分类有些不同&#xff0c;只出现了分类0的情况&#xff0c;怀疑是标注有问题&#xff0c;但是数据集太大&#xff0c;于是做了个小工具对标注…

docker:docker: Get https://registry-1.docker.io/v2/: net/http: request canceled

无数次的拉镜像让人崩溃&#xff1a; rootnode11:~/ragflow/docker# more rag.sh #export HTTP_PROXYhttp://192.168.207.127:7890 #export HTTPS_PROXYhttp://192.168.207.127:7890 #export NO_PROXYlocalhost,127.0.0.1,.aliyun.com docker compose -f docker-compose-gpu-C…

ubuntu-desktop-24.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法)

ubuntu-desktop-24.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法) 一、更新并安装基础软件 #切换root用户 sudo su -#更新 apt update #升级 apt upgrade#install vim apt install vim#install net-tools apt install net-tools二、安装ssh并设置…

UDP协议和TCP协议之间有什么具体区别?

UDP&#xff08;User Datagram Protocol&#xff09;和TCP&#xff08;Transmission Control Protocol&#xff09;是两种常见的网络传输协议&#xff0c;它们在数据传输中有着显著的区别和适用场景。理解它们的区别对于网络工程师、软件开发人员以及网络安全专家都是至关重要的…

使用Docker快速部署FastAPI Web应用

Docker是基于 Linux 内核的cgroup、namespace以及 AUFS 类的Union FS 等技术&#xff0c;对进程进行封装隔离&#xff0c;一种操作系统层面的虚拟化技术。Docker中每个容器都基于镜像Image运行&#xff0c;镜像是容器的只读模板&#xff0c;容器是模板的一个实例。镜像是分层结…