react hooks--useState
概述
useState 可以使函数组件像类组件一样拥有 state,也就说明函数组件可以通过 useState 改变 UI 视图。那么 useState 到底应该如何使用,底层又是怎么运作的呢,首先一起看一下 useState 。

问题:Hook 是什么? 一个 Hook 就是一个特殊的函数,让你在函数组件中获取状态等 React 特性
使用模式:函数组件 + Hooks
特点:从名称上看,Hook 都以 use 开头
基本使用


- 使用场景:当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 了
- 作用:为函数组件提供状态(state)
- 使用步骤:
-
- 导入
useState函数:import { useState} from 'react' - 调用
useState函数,并传入状态的初始值 - 从
useState函数的返回值中,拿到状态和修改状态的函数 - 在 JSX 中展示状态
- 在按钮的点击事件中调用修改状态的函数,来更新状态
- 导入
- 规则:
-
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用
- useState接受唯一一个参数,在第一次组件被调用时使用来作为初始化值
使用数组解构简化
比如,要获取数组中的元素:
- 原始方式:索引访问
const arr = ['aaa', 'bbb']const a = arr[0] // 获取索引为 0 的元素
const b = arr[1] // 获取索引为 1 的元素
- 简化方式:数组解构
-
- 相当于创建了两个变量(可以是任意的变量名称)分别获取到对应索引的数组元素
const arr = ['aaa', 'bbb']
const [a, b] = arrconst [state, setState] = arr
- 使用数组解构简化
useState的使用
-
- 约定:修改状态的函数名称以 set 开头,后面跟上状态的名称
// 解构出来的名称可以是任意名称
const [state, setState] = useState(0)
const [age, setAge] = useState(0)
const [count, setCount] = useState(0)
演示示例
父传子
App.js--父组件
import React from 'react'
import UseFun from './components/useFun'export default function App() {let msg = '132'return (<div><UseFun msg={msg}/></div>)
}
src/components/useFun.js--子组件
import React from 'react'
import PropTypes from 'prop-types'function useFun(props) {return (<div>{props.msg}</div>)
}useFun.defaultProps = {msg: '456'
}useFun.propTypes = {msg: PropTypes.string
}export default useFun
计数器
App.js
import React from 'react'
import UseFun from './components/UseFun'
import CountFun from './components/CountFun'export default function App() {let msg = '132'return (<div><CountFun /></div>)
}
components/CountFun.jsx
import React from 'react'
import { useState } from 'react';export default function CountFun() {let [count, setCount] = useState(0)function changeCount(){setCount(count + 1)}return (<div>{/* 展示状态值 */}<h1>useState Hook - {count}</h1>{/* 点击按钮,让状态值 +1 */}<button onClick={changeCount}>+1</button><button onClick={() => changeCount()}>+1</button></div>)
}
或下面的做法也可以
import { useState } from 'react'export default function const CountFun = () => {// 返回值是一个数组const stateArray = useState(0)// 状态值 -> 0const state = stateArray[0]// 修改状态的函数const setState = stateArray[1]return (<div>{/* 展示状态值 */}<h1>useState Hook -> {state}</h1>{/* 点击按钮,让状态值 +1 */}<button onClick={() => setState(state + 1)}>+1</button></div>)
}
- 参数:状态初始值。比如,传入 0 表示该状态的初始值为 0
-
- 注意:此处的状态可以是任意值(比如,数值、字符串等),而 class 组件中的 state 必须是对象
- 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
useState详解
概述
状态的读取和修改:
状态的使用:1 读取状态 2 修改状态
- 读取状态:该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
- 修改状态:
setCount(newValue)是一个函数,参数表示:新的状态值- 调用该函数后,将使用新的状态值
替换旧值 - 修改状态后,因为状态发生了改变,所以,该组件会重新渲染
组件的更新过程:
函数组件使用 useState hook 后的执行过程,以及状态值的变化:
- 组件第一次渲染:
-
- 从头开始执行该组件中的代码逻辑
- 调用
useState(0)将传入的参数作为状态初始值,即:0 - 渲染组件,此时,获取到的状态 count 值为: 0
- 组件第二次渲染:
-
- 点击按钮,调用
setCount(count + 1)修改状态,因为状态发生改变,所以,该组件会重新渲染 - 组件重新渲染时,会再次执行该组件中的代码逻辑
- 再次调用
useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1 - 再次渲染组件,此时,获取到的状态 count 值为:1
- 点击按钮,调用
注意:useState 的初始值(参数)只会在组件第一次渲染时生效。
也就是说,以后的每次渲染,useState 获取到都是最新的状态值。React 组件会记住每次最新的状态值!
注意点:
- 在普通函数(方法)中,不能使用hooks
- 在自定义的hooks中可以使用,自定义hooks必须以use开头
基本数据类型
引入useState
import { useState } from 'react';
在函数组件内部定义state数据
let [count, setCount] = useState(0);
let [name, setName] = useState('张三')
useState会返回一个数组,数组有两个值
- 第一个值:代表state数据
- 第二个值:更新这个state数据的一个函数,一般函数变量取名字通常是
setXxx
可以定义多个 state值
修改数据:
function changeName() {setName('李四');
}
完整代码:
import React from 'react'
import { useState } from 'react';function useFun(props) {let [msg, setMsg] = useState('张三')function changeMsg(){setMsg('李四')}return (<div><p>msg:{msg}</p><button onClick={changeMsg}>修改msg</button></div>)
}export default useFun
引用数据类型
定义对象数据
let [user, setUser] = useState({name: 'zs',age: 20
})
修改对象
function changeUserName() {setUser({name: 'ls',})
}
这样修改对象,会导致其他属性丢失,因为它没有自动合并对象
正确方式:
function changeUserName() {setUser({...user,name: 'ls',})
}
可以将整个对象解构扩展,然后将修改的属性覆盖原来属性
注意:
修改state数据第一个参数可以是函数
setUser((prevState) => {console.log(prevState)return {...prevState,name: 'ls'}
})
这个函数和类组件的setState的第一个参数是一致的
修改数据函数没有第二个参数
修改数据的函数仍然是异步的,不是同步的。
完整代码:
import React from "react";
import { useState } from "react";function useFun(props) {let [msg, setMsg] = useState("张三");let [user, setName] = useState({name: "zs",age: 20,});function changeMsg() {setMsg("李四");}const changeName = () => {// setName({// ...user,// name: 'ls'// })//或如下写法:setName((prevState) => {console.log(prevState);return {...prevState,name: "ls",};});};return (<div><p>msg:{msg}</p><button onClick={changeMsg}>修改msg</button><hr /><p>name::{user.name}</p><p>age: {user.age}</p><button onClick={changeName}>修改name</button></div>);
}export default useFun;
为函数组件添加多个状态
问题:如果一个函数组件需要多个状态,该如何处理?
回答:调用 useState Hook 多次即可,每调用一次 useState Hook 可以提供一个状态。
注意:useState Hook 多次调用返回的 [state, setState] 相互之间,互不影响。
注意:React Hooks 只能直接出现在 函数组件 中,不能嵌套在 if/for/其他函数中!
否则就会报错:React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render
React 的 useState 这个 Hook 被条件性(放在一个条件判断中)的调用了。
React Hooks 必须要每次组件渲染时,按照相同的顺序来调用所有的 Hooks。
- 为什么会有这样的规则? 因为 React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个 Hook
- 通过开发者工具可以查看到。
如何监听 state 变化?
类组件 setState 中,有第二个参数 callback 或者是生命周期componentDidUpdate 可以检测监听到 state 改变或是组件更新。
那么在函数组件中,如何怎么监听 state 变化呢?这个时候就需要 useEffect 出场了,通常可以把 state 作为依赖项传入 useEffect 第二个参数 deps ,但是注意 useEffect 初始化会默认执行一次。
具体可以参考如下 Demo :


dispatch更新特点
上述讲的批量更新和 flushSync ,在函数组件中,dispatch 更新效果和类组件是一样的,但是 useState 有一点值得注意,就是当调用改变 state 的函数dispatch,在本次函数执行上下文中,是获取不到最新的 state 值的,把上述demo 如下这么改:

结果:0,0,0
原因很简单,函数组件更新就是函数的执行,在函数一次执行过程中,函数内部所有变量重新声明,所以改变的 state ,只有在下一次函数组件执行时才会被更新。所以在如上同一个函数执行上下文中,number 一直为0,无论怎么打印,都拿不到最新的 state 。
useState注意事项
在使用 useState 的 dispatchAction 更新 state 的时候,记得不要传入相同的 state,这样会使视图不更新。比如下面这么写:

如上例子🌰中,当点击按钮后,发现视图没有改变,为什么会造成这个原因呢?
在 useState 的 dispatchAction 处理逻辑中,会浅比较两次 state ,发现 state 相同,不会开启更新调度任务; demo 中两次 state 指向了相同的内存空间,所以默认为 state 相等,就不会发生视图更新了。
解决问题: 把上述的 dispatchState 改成 dispatchState({...state}) 根本解决了问题,浅拷贝了对象,重新申请了一个内存空间。
相关文章:
react hooks--useState
概述 useState 可以使函数组件像类组件一样拥有 state,也就说明函数组件可以通过 useState 改变 UI 视图。那么 useState 到底应该如何使用,底层又是怎么运作的呢,首先一起看一下 useState 。 问题:Hook 是什么? 一个 Hook 就是…...
C/C++:优选算法(持续更新~~)
一、双指针 1.1移动零 链接:283. 移动零 - 力扣(LeetCode) 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操…...
【qt信号槽-6】槽函数不执行的一种原因——未知线程
背景: 项目需要调用第三方库,又要涉及多线程,遇到了在connect成功之后,槽函数依然不执行的情况。按照常理,槽函数不执行无非就几种情况: 要么connect未成功。 要么disconnect,或者对象被销毁…...
Leetcode面试经典150题-162.寻找峰值
解法都在代码里,不懂就留言或者私信 想清楚的话会特别简单,你可能想不到这是个二分。。。 class Solution {/**本题题目规定我们只能用O(logN)的时间复杂度来解题,这显然就是让二分嘛而题目给的数组本身是无需,怎么二分呢其实我…...
Vue组件:模板引用ref属性的使用
Vue 组件系列文章: 《Vue组件:创建组件、注册组件、使用组件》 《Vue组件:使用Prop实现父组件向子组件传递数据》 《Vue组件:使用$emit()方法监听子组件事件》 《Vue组件:插槽》 《Vue组件:混入》 《Vue组件…...
robomimic基础教程(一)——基本概念
robosuite和robomimic都是由ARISE Initiative开发的开源工具,旨在推进机器人学习和机器人操作领域的研究。 一、基本概念 robomimic是一个用于机器人示范学习的框架。它提供了在机器人操作领域收集的大量示范数据集,以及用于从这些数据集中学习的离线学…...
7天速成前端 ------学习日志 (继苍穹外卖之后)
前端速成计划总结: 全26h课程,包含html,css,js,vue3,预计7天内学完。 起始日期:9.16 预计截止:9.22 每日更新,学完为止。 学前计划 课…...
讲课研判:基于教师上课视频文件的综合分析
在教育评估与改进的过程中,对教师上课视频文件进行详尽的研判是一项至关重要的工作。它不仅能够帮助教师自我反思、提升教学质量,还能为教育管理者提供决策依据,促进教育教学的整体优化。本文将从教学目标、教学内容、教学效果、教学能力、教…...
mac 如何开启指定端口供外部访问?
前言 需要 mac 上开放指定端口,指定 ip 访问 解决 在 macOS 上开放一个端口,并指定只能特定的 IP 访问,可以使用 macOS 内置的 pfctl(Packet Filter)工具来实现。 1、 编辑 pf 配置文件: 打开 /etc/pf.conf 文件进行编辑。 可以使…...
Weblogic部署
要安装weblogic,首先要有java环境,因此需要先安装jdk。 这里需要注意,weblogic版本不同,对应的jdk版本也不同,我在这里就踩了很多坑,我这里下载的是fmw_12.2.1.4.0_wls_lite_generic.jar对应的是jdk-8u333…...
面向对象设计的五大原则(SOLID 原则)
面向对象设计的五大原则(SOLID 原则)是指导我们设计可维护、灵活且易扩展的面向对象系统的核心准则。这些原则帮助开发者避免常见的设计陷阱,使代码更具可读性和可维护性。 0.设计原则和设计模式的关系 设计原则(Design Princip…...
Python和MATLAB及C++信噪比导图(算法模型)
🎯要点 视频图像修复模数转换中混合信号链噪音测量频谱计算和量化周期性视觉刺激脑电图高斯噪声的矩形脉冲 总谐波失真 周期图功率谱密度各种心率失常检测算法胶体悬浮液跟踪检测计算交通监控摄像头图像噪音计算 Python信噪比 信噪比是科学和工程中使用的一种测…...
App及web反编译方案
APP反编译代码的工具下载: 下载地址:APK逆向三件套apktool-2.9.3.jar,dex2jar-2.0.zip,jd-gui-windows-1.6.6资源-CSDN文库 》dex2jar: 把dex文件转成jar文件 》 jd-gui: 这个工具用于将jar文件转换成java代码 》APKTool: 首先把…...
学成在线练习(HTML+CSS)
准备工作 项目目录 内部包含当前网站的所有素材,包含 HTML、CSS、图片、JavaScript等等 1.由于元素具有一些默认样式,可能是我们写网页过程中根本不需要的,所有我们可以在写代码之前就将其清除 base.css /* 基础公共样式:清除…...
istio中使用serviceentry结合egressgateway实现多版本路由
假设有一个外部服务,外部服务ip为:10.10.102.90,其中32033为v1版本,32034为v2版本。 现在需要把这个服务引入到istio中,使用egressgateway转发访问该服务的流量,并且需要实现多版本路由,使得he…...
Java项目——苍穹外卖(二)
Redis 简介 Redis是一个基于内存的key-value结构数据库 基于内存存储,读写性能高适合存储热点数据(热点商品、资讯、新闻)企业应用广泛 基础操作 启动 在redis安装目录中打开cmd,输入如上图指令即可启动,按下crtl…...
【Python日志功能】三.日志记录方法与多模块日志
文章目录 相关链接第三篇:日志记录方法与多模块日志1 基本日志记录方法2 在多个模块中使用日志3 文章总结 相关链接 【Python日志功能】一.日志基础与基本配置【Python日志功能】二.高级配置与日志处理器【Python日志功能】三.日志记录方法与多模块日志官方文档&am…...
在pycharm终端中运行pip命令安装模块时,出现了“你要如何打开这个文件”弹出窗口,是什么状况?
这种情况发生在Windows系统上,当在PyCharm终端中运行pip命令安装模块时,如果系统无法确定要使用哪个程序打开该文件,就会出现“你要如何打开这个文件”弹出窗口。 解决方法是: 选择“查找一个应用于此文件”的选项。在弹出的窗口…...
Axure多人协调的方式
当系统有多个模块,又由不同的产品经理负责设计,如何进行协调? 尝试过的方法 1)搭建Axure私服,用Axure的私服进行一个RP文件多人协同编辑; 2)用SVN管理RP文件,每次都要合并。 以上…...
【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程
【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【OnnxRuntime】【Python】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转on…...
用Python+OpenCV手把手实现Prewitt边缘检测(附完整代码与效果对比图)
用PythonOpenCV手把手实现Prewitt边缘检测(附完整代码与效果对比图) 边缘检测是计算机视觉中最基础也最关键的预处理步骤之一。想象一下,当你需要让计算机"看清"一张照片中的物体轮廓时,边缘检测算法就是它的"视觉…...
用Python和MNE库玩转BCI Competition IV 2a脑电数据集:从数据加载到可视化全流程
用Python和MNE库玩转BCI Competition IV 2a脑电数据集:从数据加载到可视化全流程当你第一次接触脑电信号处理时,面对原始数据文件可能会感到无从下手。BCI Competition IV 2a数据集作为脑机接口领域的经典基准数据,包含了9名受试者四种运动想…...
终极艾尔登法环帧率解锁指南:轻松突破60FPS限制
终极艾尔登法环帧率解锁指南:轻松突破60FPS限制 【免费下载链接】EldenRingFpsUnlockAndMore A small utility to remove frame rate limit, change FOV, add widescreen support and more for Elden Ring 项目地址: https://gitcode.com/gh_mirrors/el/EldenRing…...
美团外卖mtgsig与waimai_sign双层签名逆向解析
1. 这不是“爬虫教程”,而是一份反向工程现场笔记你搜到这篇内容,大概率正卡在某个调试窗口前:抓包看到mtgsig和waimai_sign两个参数像两堵墙,无论怎么改请求头、换UA、清缓存,返回永远是{"code":403,"…...
flameshow性能优化技巧:如何快速定位Go程序中的CPU热点
flameshow性能优化技巧:如何快速定位Go程序中的CPU热点 【免费下载链接】flameshow A terminal Flamegraph viewer. 项目地址: https://gitcode.com/gh_mirrors/fl/flameshow 🔥 想要快速定位Go程序中的性能瓶颈吗?flameshow是一个强大…...
用Azure Kinect DK和Body Tracking SDK,5分钟实现一个实时人体骨骼点检测Demo(C++版)
5分钟实战:用Azure Kinect DK实现实时人体骨骼点追踪(C版) 当你第一次拿到Azure Kinect DK时,最令人兴奋的莫过于它强大的人体追踪能力。这款深度相机不仅能捕捉高清彩色图像,更能通过AI算法实时重建人体骨骼关节点。本…...
告别KITTI!用TartanAir数据集在Unreal Engine+AirSim里复现那些让VSLAM算法“翻车”的雨天和黑夜
超越KITTI:用TartanAir数据集在虚拟极端环境中锤炼VSLAM算法当视觉SLAM算法在KITTI数据集上取得95%的准确率时,开发者们常常会松一口气——直到这些算法被部署到真实世界的雨夜街道上。突然之间,那些在阳光明媚的德国道路上表现优异的特征点检…...
3步开启Windows 11安卓应用新体验:WSA完整使用指南
3步开启Windows 11安卓应用新体验:WSA完整使用指南 【免费下载链接】WSA Developer-related issues and feature requests for Windows Subsystem for Android 项目地址: https://gitcode.com/gh_mirrors/ws/WSA Windows Subsystem for Android(简…...
C语言预处理指令全解析
第六章 预处理命令在c语言中,所有# 开头的指令,被称为预处理指令。gcc 编译预处理 所有的预处理指令,都要在这步处理完汇编编译连接#include包含头文件。 全局变量的声明,函数的声明, 自定义构造类型声明, …...
体验低延迟与高稳定性的大模型 API 聚合服务调用感受
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 体验低延迟与高稳定性的大模型 API 聚合服务调用感受 在集成大模型能力到实际应用的过程中,开发者最关心的往往是两个核…...
