react hooks--useState

概述

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

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

基本使用

  • 使用场景:当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 了
  • 作用:为函数组件提供状态(state)
  • 使用步骤:
    1. 导入 useState 函数:import { useState} from 'react'
    2. 调用 useState 函数,并传入状态的初始值
    3. useState 函数的返回值中,拿到状态和修改状态的函数
    4. 在 JSX 中展示状态
    5. 在按钮的点击事件中调用修改状态的函数,来更新状态
  • 规则:
    • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用
    • 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用
  • useState接受唯一一个参数,在第一次组件被调用时使用来作为初始化值

使用数组解构简化

比如,要获取数组中的元素:

  1. 原始方式:索引访问
const arr = ['aaa', 'bbb']const a = arr[0]  // 获取索引为 0 的元素
const b = arr[1]  // 获取索引为 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 修改状态

  1. 读取状态:该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
  2. 修改状态:
  • setCount(newValue) 是一个函数,参数表示:新的状态值
  • 调用该函数后,将使用新的状态值替换旧值
  • 修改状态后,因为状态发生了改变,所以,该组件会重新渲染

组件的更新过程:

函数组件使用 useState hook 后的执行过程,以及状态值的变化:

  • 组件第一次渲染:
    1. 从头开始执行该组件中的代码逻辑
    2. 调用 useState(0) 将传入的参数作为状态初始值,即:0
    3. 渲染组件,此时,获取到的状态 count 值为: 0
  • 组件第二次渲染:
    1. 点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染
    2. 组件重新渲染时,会再次执行该组件中的代码逻辑
    3. 再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1
    4. 再次渲染组件,此时,获取到的状态 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}) 根本解决了问题,浅拷贝了对象,重新申请了一个内存空间。

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

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

相关文章

TensorRT-LLM——优化大型语言模型推理以实现最大性能的综合指南

引言 随着对大型语言模型 (LLM) 的需求不断增长&#xff0c;确保快速、高效和可扩展的推理变得比以往任何时候都更加重要。NVIDIA 的 TensorRT-LLM 通过提供一套专为 LLM 推理设计的强大工具和优化&#xff0c;TensorRT-LLM 可以应对这一挑战。TensorRT-LLM 提供了一系列令人印…

Double Write

优质博文&#xff1a;IT-BLOG-CN 一、存在的问题 为什么需要Double Write&#xff1a; InnoDB的PageSize是16kb&#xff0c;其数据校验也是针对这16KB来计算的&#xff0c;将数据写入磁盘是以Page为单位的进行操作的。而计算机硬件和操作系统&#xff0c;写文件是以4KB作为基…

Python基础语法(1)上

常量和表达式 我们可以把 Python 当成一个计算器&#xff0c;来进行一些算术运算。 print(1 2 - 3) print(1 2 * 3) print(1 2 / 3) 这里我们可能会有疑问&#xff0c;为什么不是1.6666666666666667呢&#xff1f; 其实在编程中&#xff0c;一般没有“四舍五入”这样的规则…

基于Python DoIPClient库的DoIP上位机开发手顺

代码 address, announcement DoIPClient.await_vehicle_announcement()logical_address announcement.logical_addressip, port addressprint(ip, port, logical_address) 效果 代码 address, announcement DoIPClient.get_entity(ecu_ip_addresssIp, protocol_version3…

二叉树OJ题——相同的树

文章目录 一、题目链接二、解题思路三、解题代码 一、题目链接 相同的树 二、解题思路 时间复杂度&#xff1a;O(min(n,m)) 三、解题代码

解决IDEA每次创建新项目时都要指定Maven仓库和Maven配置文件的问题

文章目录 0. 前言1. 打开新项目的设置2. 搜索 Maven 相关的配置3. 更改Maven主路径、配置文件、本地仓库4. 更改新项目的Maven配置后没生效 0. 前言 在 IDEA 中每次创建新项目时&#xff0c;使用的都是默认的 Maven 仓库和默认的配置文件&#xff0c;需要我们手动修改&#xf…

利用AI驱动智能BI数据可视化-深度评测Amazon Quicksight(三)

简介 随着生成式人工智能的兴起&#xff0c;传统的 BI 报表功能已经无法满足用户对于自动化和智能化的需求&#xff0c;今天我们将介绍亚马逊云科技平台上的AI驱动数据可视化神器 – Quicksight&#xff0c;利用生成式AI的能力来加速业务决策&#xff0c;从而提高业务生产力。…

SpringSecurity原理解析(八):CSRF防御解析

一、CsrfFilter CsrfFilter 主要功能是用来防止csrf攻击 一、什么是CSRF攻击 跨站请求伪造&#xff08;英语&#xff1a;Cross-site request forgery&#xff09;&#xff0c;也被称为 one-click attack 或者 session riding&#xff0c;通常缩写为 CSRF 或者 XSRF&#xff0c…

【IP协议】IP协议报头结构

文章目录 IP 协议报头结构4位版本4位首部长度8位服务类型16位总长度16位标识、3位标志、13位片偏移8位生存时间8位协议16位首部校验和32源 IP 地址、32位目的 IP 地址 IP 协议报头结构 4位版本 实际上只有两个取值 4 > IPv4&#xff08;主流&#xff09;6 > IPv6 IPv2&…

浅谈人工智能之基于ollama本地大模型结合本地知识库搭建智能客服

浅谈人工智能之基于ollama本地大模型结合本地知识库搭建智能客服 摘要 随着人工智能技术的飞速发展,基于大型语言模型(LLMs)的智能客服系统逐渐成为提升企业服务质量和效率的关键工具。然而,对于注重数据隐私和安全的企业而言,使用云服务可能会引发数据泄露的风险。因此…

【C++题解】1996. 每个小组的最大年龄

欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 问题&#xff1a;1996. 每个小组的最大年龄 类型&#xff1a;二维数组 题目描述&#xff1a; 同学们在操场上排成了一个 n 行 m 列的队形&#xff0c;每行的同学属于一个小组&#xff0c;请问每个小…

2022高教社杯全国大学生数学建模竞赛C题 问题一(1) Python代码演示

目录 问题 11.1 对这些玻璃文物的表面风化与其玻璃类型、纹饰和颜色的关系进行分析数据探索 -- 单个分类变量的绘图树形图条形图扇形图雷达图Cramer’s V 相关分析统计检验列联表分析卡方检验Fisher检验绘图堆积条形图分组条形图分类模型Logistic回归随机森林import matplotlib…

SPI学习笔记

SPI SPI是一种同步串行通信接口规范&#xff0c;它允许一个主设备与一个或多个从设备进行全双工通信。SPI用于短距离通信&#xff0c;主要应用于嵌入式系统。 SPI通信过程 1.初始化&#xff1a;SPI主机首先将SS或CS线拉低&#xff0c;以选择特定的从设备并开始通信。 2.数据…

linux文件系统权限详解

注:目录的执行权限代表是否可以进入。 一、文件权限控制对文件的访问: 可以针对文件所属用户、所属组和其他用户可以设置不同的权限 权限具有优先级。user权限覆盖group权限,后者覆盖other权限。 有三种权限类别:读取、写入和执行 读权限:对文件:可读取文件…

集群聊天服务器项目【C++】(五)网络模块和业务模块

经过前面介绍相关的库和工具&#xff0c;比如Json、CMake、muduo等&#xff0c;我们可以开始编写本项目的代码了。 1.项目目录创建 一般一个项目由以下结构组成&#xff1a; bin文件夹存放&#xff1a;可执行程序build文件夹存放&#xff1a;编译过程中的临时文件include文…

电子竞技信息交流平台|基于java的电子竞技信息交流平台系统小程序(源码+数据库+文档)

电子竞技信息交流平台系统小程序 目录 基于java的电子竞技信息交流平台系统小程序 一、前言 二、系统设计 三、系统功能设计 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设…

“拍照赚钱”的任务定价(2017数学建模国赛b题)

文章目录 题目说明解题思路第一问第二问第三问第四问 部分结果图项目地址 题目 赛题地址 说明 数模国赛前的练手题。其实我个人感觉这道题很散&#xff0c;都是找一些规律进行总结统计&#xff0c;最多结合一些机器学习算法进行预测拟合之类的我刚开始用matlab&#xff0c;后…

【演化博弈论】:双方演化博弈的原理与过程

目录 一、演化博弈的原理1. 基本概念2. 参与者的策略3.演化过程 二、MATLAB 代码解读&#xff08;博弈参与主体&#xff08;双方&#xff09;策略选择的动态演化讨程&#xff09;三、MATLAB 代码解读&#xff08;博弈主体随着时间策略选择的动态演化讨程&#xff09;四、结论 演…

Java 枚举 新特性

Java 枚举&#xff08;enum&#xff09;自JDK 1.5引入以来&#xff0c;随着版本的升级不断增强。本文将回顾枚举的演进&#xff0c;尤其是结合switch语句的应用&#xff0c;展示枚举如何在现代Java中变得更加灵活。 1. JDK 1.5&#xff1a;Java 枚举的诞生 在JDK 1.5之前&…

TAG:BladeLLM 的纯异步推理架构

作者&#xff1a;张子鹏 PAI引擎团队 随着 GQA/MLA/MoE 等模型结构不断发展&#xff0c;大语言模型的推理逐步解除了显存限制&#xff0c;逐渐向着高并发、高吞吐的方向发展。推理引擎的运行时开销也变得不可忽视。主流 LLM 推理框架的运行时开销大致来自&#xff1a; Python …