React-useState讲解

embedded/2025/1/17 15:33:54/

useState

让页面“动”起来

例如实现一个 click 计数功能,普通变量无法实现。即:修改普通变量无法触发组件的更新 rerender

通过 useState 即可实现。

state 是什么

State, A component’s memory —— 这个比喻非常好!

  • props 父组件传递过来的信息
  • state 组件自己内部的状态,不对外

每次 state 变化,都会触发组件更新,从新渲染页面。

代码演示,参考 react-ts-demo 中 pages/StateDemo1.tsx

import React, { FC, useState } from 'react'const Demo: FC = () => {// let count = 0 // 普通的 js 变量,无法触发组件的更新const [count, setCount] = useState(0) // useState 可以触发组件的更新,//   const [name, setName] = useState('双越')function add() {// count++// setCount(count + 1)setCount(count => count + 1) // 使用函数,state 更新不会被合并setCount(count => count + 1)setCount(count => count + 1)setCount(count => count + 1)setCount(count => count + 1)// setCount(count => count + 1)console.log('cur count ', count) // 异步更新,无法直接拿到最新的 state 值}return (<div><button onClick={add}>add {count}</button></div>)
}export default Demo

state 的特点

异步更新

代码演示

PS:setState 传入函数,可同步更新

可能会被合并

代码演示

不可变数据

state 可以是任意 JS 类型,不仅仅是值类型。

不可直接修改 state ,而要 setState 新值。

代码演示

PS:函数组件,每个更新函数从新执行,state 被重置,而不是被修改。state 可以理解为 readOnly

immer

Immer 简化了不可变数据结构的处理。特别是对于 JS 语法没那么熟悉的人。

代码演示,参考 react-ts-demo 中 pages/ImmerDemo1.tsx

import React, { FC, useState } from 'react'
import produce from 'immer'const Demo: FC = () => {//   const [userInfo, setUserInfo] = useState({ name: '柚子', age: 20 })//   function changeAge() {//     // // **不可变数据** - 不去修改 state 的值,而是要传入一个新的值 —— 重要!//     // setUserInfo({//     //   ...userInfo,//     //   age: 21,//     // })//     setUserInfo(//       produce(draft => {//         draft.age = 21//       })//     )//   }const [list, setList] = useState(['x', 'y'])function addItem() {// // **不可变数据** - 不去修改 state 的值,而是要传入一个新的值 —— 重要!// setList(list.concat('z'))// // setList([...list, 'z'])setList(produce(draft => {draft.push('z')}))}return (<div><h2>state 不可变数据</h2>{/* <div>{JSON.stringify(userInfo)}</div><button onClick={changeAge}>change age</button> */}<div>{JSON.stringify(list)}</div><button onClick={addItem}>add item</button></div>)
}export default Demo

实战:List 页面使用 state

  • 使用 state
  • 使用 immer
    • push
    • 修改 isPublish

代码参考 pages/List2.tsx

import React, { FC, useState, useEffect } from 'react'
import produce from 'immer'
import QuestionCard from './components/QuestionCard'// 组件是一个函数(执行返回 JSX 片段),组件初次渲染执行这个函数
// 任何 state 更新,都会触发组件的更新(重新执行函数)
const List2: FC = () => {useEffect(() => {console.log('加载 ajax 网络请求')return () => {console.log('销毁')}}, []) // 无依赖,组件初次渲染时执行// const [count, setCount] = useState(0)const [questionList, setQuestionList] = useState([{ id: 'q1', title: '问卷1', isPublished: false },{ id: 'q2', title: '问卷2', isPublished: true },{ id: 'q3', title: '问卷3', isPublished: false },{ id: 'q4', title: '问卷4', isPublished: true },])// useEffect(() => {//   console.log('question list changed')// }, [questionList])// useEffect(() => {//   console.log('count changed')// }, [count, questionList])function add() {// setCount(count + 1)const r = Math.random().toString().slice(-3)// setQuestionList(//   // 新增 concat//   questionList.concat({//     id: 'q' + r,//     title: '问卷' + r,//     isPublished: false,//   })// )// immer 的方式setQuestionList(produce(draft => {draft.push({id: 'q' + r,title: '问卷' + r,isPublished: false,})}))}function deleteQuestion(id: string) {// // 不可变数据// setQuestionList(//   // 删除 filter//   questionList.filter(q => {//     if (q.id === id) return false//     else return true//   })// )// immer 的方式setQuestionList(produce(draft => {const index = draft.findIndex(q => q.id === id)draft.splice(index, 1)}))}function publishQuestion(id: string) {// setQuestionList(//   // 修改 map//   questionList.map(q => {//     if (q.id !== id) return q//     return {//       ...q,//       isPublished: true,//     }//   })// )// immer 的方式setQuestionList(produce(draft => {const q = draft.find(item => item.id === id)if (q) q.isPublished = true}))}return (<div><h1>问卷列表页2</h1><div>{questionList.map(question => {const { id, title, isPublished } = questionreturn (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}deleteQuestion={deleteQuestion}publishQuestion={publishQuestion}/>)})}</div><div><button onClick={add}>新增问卷</button></div></div>)
}export default List2

代码参考 /components/QuestionCard

import React, { FC, useEffect } from 'react'
import classnames from 'classnames'
// import './QuestionCard.css'
import styles from './QuestionCard.module.scss'// ts 自定义类型
type PropsType = {id: stringtitle: stringisPublished: booleandeleteQuestion?: (id: string) => voidpublishQuestion?: (id: string) => void
}// FC - functional component
const QuestionCard: FC<PropsType> = props => {const { id, title, isPublished, deleteQuestion, publishQuestion } = propsfunction publish(id: string) {publishQuestion && publishQuestion(id)}function del(id: string) {deleteQuestion && deleteQuestion(id)}// useEffect(() => {//   console.log('question card mounted')//   return () => {//     console.log('question card unmounted', id) // 销毁//   }//   // 生命周期:创建,更新(state 变化),销毁// }, [])// let itemClassName = 'list-item'// if (isPublished) itemClassName += ' published'// // 逻辑稍微复杂// const itemClassName = classnames('list-item', { published: isPublished })// const itemClassName = classnames({//   'list-item': true,//   published: isPublished,// })const listItemClass = styles['list-item']const publishedClass = styles.publishedconst itemClassName = classnames({[listItemClass]: true,[publishedClass]: isPublished,})return (<div key={id} className={itemClassName}><strong>{title}</strong>&nbsp;{/* 条件判断 */}{isPublished ? <span className={styles['published-span']}>已发布</span> : <span>未发布</span>}&nbsp;<buttononClick={() => {publish(id)}}>发布问卷</button>&nbsp;<buttononClick={() => {del(id)}}>删除问卷</button></div>)
}export default QuestionCard

最重要的就是:不可变数据 —— 这是 React state 的核心


http://www.ppmy.cn/embedded/154691.html

相关文章

vscode【实用插件】Material Icon Theme 美化文件图标

安装 在 vscode 插件市场的搜索 Material Icon Theme点 安装 效果

宝安区石岩街道阳台山的山泉水探寻

今天七彩城堡儿童乐园的小朋友比较少&#xff0c;娃没有玩尽兴。于是带娃去阳台山爬山。娃说想去山泉水方向爬山。这次我特意看了下时间&#xff0c;从阳台山门口到山泉水取水点&#xff0c;实际步行8分钟&#xff0c;应该大部分人10分钟内就可以到山泉水的位置&#xff0c;怪不…

Bootstrap 下拉菜单

Bootstrap 下拉菜单 Bootstrap 是一个流行的前端框架&#xff0c;它提供了许多预构建的组件&#xff0c;其中之一就是下拉菜单。下拉菜单是一个交互式元素&#xff0c;允许用户从一系列选项中选择一个。在本篇文章中&#xff0c;我们将详细介绍如何在 Bootstrap 中创建和使用下…

【老白学 Java】线程的并发问题(一)

线程的并发问题&#xff08;一&#xff09; 文章来源&#xff1a;《Head First Java》修炼感悟。 尽管多线程设计很令人兴奋&#xff0c;但还是要尽可能「避坑」。 想象一下&#xff0c;当你用精心设计的两个线程同时维护某个对象数据时&#xff0c;这个数据安全吗&#xff1f…

QT开发技术 【基于TinyXml2的对类进行序列化和反序列化】一

一、对TinyXml2 进行封装 使用宏 实现序列化和反序列化 思路&#xff1a; 利用宏增加一个类函数&#xff0c;使用序列化器调用函数进行序列化 封装宏示例 #define XML_SERIALIZER_BEGIN(ClassName) \ public: \virtual void ToXml(XMLElement* parentElem, bool bSerialize …

3、C#基于.net framework的应用开发实战编程 - 实现(三、一) - 编程手把手系列文章...

三、 实现&#xff1b; 三&#xff0e;一、实现数据库操作&#xff1b; 对于数据库的操作&#xff0c;以前都是有ODBC的接口&#xff0c;通过Helper类库进行的操作。此文主要介绍例子里对数据库操作的实现。 1、 SQLiteHelper&#xff1b; SQLite主要是用C编写的&#xff0c;但…

【概率论与数理统计】第三章 多维随机变量及其分布(2)

定义7&#xff1a;若二维连续型随机变量 ( X , Y ) (X,Y) (X,Y)的概率密度为&#xff1a; f ( x , y ) 1 2 π σ 1 σ 2 1 − ρ 2 e − 1 2 ( 1 − ρ 2 ) [ ( x − μ 1 ) 2 σ 1 2 − 2 ρ ( x − μ 1 ) ( y − μ 2 ) σ 1 σ 2 ( y − μ 2 ) 2 σ 2 2 ] f(x,y) \fra…

(经过验证)在 Ubuntu 系统中为 VSCode、PyCharm 终端及 Jupyter Notebook 配置代理的完整方案

文章目录 1. 通过系统环境变量配置代理步骤一&#xff1a;打开终端步骤二&#xff1a;编辑 ~/.bashrc 文件步骤三&#xff1a;添加代理环境变量步骤四&#xff1a;保存并关闭文件步骤五&#xff1a;使配置生效步骤六&#xff1a;重启相关应用步骤七&#xff1a;使用代理函数 2.…