文章目录
- 1 useState
- 1.1 说明
- 返回
- 1.2 示例
- 1.3 数据类型
- 2 state
- 2.1 概述
- 2.2 state特点
- 3 state重构问卷
- 4 immer
- 结语
1 useState
useState
是一个 React Hook,它允许你向组件添加一个 状态变量。
1.1 说明
- 语法
const [state, setState] = useState(initialState)
- 参数 initialState: 你希望 state 初始化的值。它可以是任何类型的值,但对于函数有特殊的行为。在初始渲染后,此参数将被忽略。
- 如果传递函数作为
initialState
,则它将被视为 初始化函数。它应该是纯函数,不应该接受任何参数,并且应该返回一个任何类型的值。当初始化组件时,React 将调用你的初始化函数,并将其返回值存储为初始状态。
返回
useState
返回一个由两个值组成的数组:
- 当前的 state。在首次渲染时,它将与你传递的
initialState
相匹配。 set
函数,它可以让你将 state 更新为不同的值并触发重新渲染。
1.2 示例
如下是一个简单的点击按钮累加的示例:
import { useState } from "react";
function Acc() {// 普通js变量无法触发组件更新// let count = 0;//useState可以触发组件更新const [count, setCount] = useState(0);// 点击累加function add() {setCount(count + 1);}return (<><button onClick={add}> click to accumulate:{count} </button></>);
}export default Acc;
普通js变量无法触发组件更新,如下图所示:
使用useState实现页面组件更新,如下图所示:
1.3 数据类型
useState(0)
useState('a')
useState({"a": 1, "b": 2})
useState([1,2,3])
- xxx类型没有限制,可以是任意类型
2 state
2.1 概述
组件可以拥有状态(state),它是组件数据的私有部分,可以用来管理动态数据。
状态仅适用于类组件,或者使用 React 的 Hook 时可以在函数组件中使用。
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。
2.2 state特点
-
State异步更新,示例代码如下
import { FC, useState } from "react"; const StateDemo01: FC = () => {//useState可以触发组件更新const [count, setCount] = useState(0);const [name, setName] = useState("张三");// 点击累加function add() {setCount(count + 1);console.log(count);//如果一个变量不用于jsx中显示,不要用useState管理它,用useRef.setName("李四");}return (<><button onClick={add}> click to accumulate:{count} </button></>); };export default StateDemo01;
打印结果如下所示:
-
state可能会被合并,
// 点击累加function add() {setCount(count + 1);setCount(count + 1);setCount(count + 1);setCount(count + 1);setCount(count + 1);console.log(count);//如果一个变量不用于jsx中显示,不要用useState管理它,用useRef.// setName("李四");}
页面结果如上所示,如果想要不被合并,参数传入函数,代码如下:
setCount((count) => count + 1);
-
不可变数据,如需修改数据,需要传入新值
import { FC, useState } from "react"; const StateDemo02: FC = () => {//useState可以触发组件更新const [userInfo, setUserInfo] = useState({ name: "张三", age: 20 });// 点击累加function edit() {// 不可变数据,不去修改state的值,而是传入一个新的值或者返回一个新值的函数或者表达式// setUserInfo({ name: "张三", age: 21 }); // 全量语法setUserInfo({ ...userInfo, age: 21 }); // 结构语法}return (<><h3>state:不可变数据</h3><div> {JSON.stringify(userInfo)} </div><button onClick={edit}> 修改年龄 </button></>); };export default StateDemo02;
修改数组
setXxx([...arr, 5]) setXxx(arr.concat(5))
3 state重构问卷
-
state替换文件列表
import { FC, useState } from "react";import QuestionCard from "./QuestionCard"; const List2: FC = () => {//列表页//问卷列表数据const questions = [{ id: "q1", title: "问卷1", isPublished: false },{ id: "q2", title: "问卷2", isPublished: false },{ id: "q3", title: "问卷3", isPublished: true },{ id: "q4", title: "问卷4", isPublished: false },{ id: "q5", title: "问卷5", isPublished: true },];const [questionList, setQuestionList] = useState(questions);const r = Math.random().toString().slice(-3);return (<div><h1>问卷列表页</h1><div>{questionList.map((q) => {const { id, title, isPublished } = q;return (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}/>);})}</div></div>); };export default List2;
-
新增问卷
import { FC, useState } from "react";import QuestionCard from "./QuestionCard"; const List2: FC = () => {//列表页//问卷列表数据const questions = [{ id: "q1", title: "问卷1", isPublished: false },{ id: "q2", title: "问卷2", isPublished: false },{ id: "q3", title: "问卷3", isPublished: true },{ id: "q4", title: "问卷4", isPublished: false },{ id: "q5", title: "问卷5", isPublished: true },];const [questionList, setQuestionList] = useState(questions);const r = Math.random().toString().slice(-3);// 新增问卷function add() {setQuestionList([...questionList,{id: "q" + r,title: "问卷" + r,isPublished: false,},]);}return (<div><h1>问卷列表页</h1><div>{questionList.map((q) => {const { id, title, isPublished } = q;return (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}/>);})}</div><div><button onClick={add}>新增问卷</button></div></div>); };export default List2;
效果如下所示:
- 删除问卷
List2.tsx代码如下:
import { FC, useState } from "react";import QuestionCard from "./components/QuestionCard"; const List2: FC = () => {//列表页//问卷列表数据const questions = [{ id: "q1", title: "问卷1", isPublished: false },{ id: "q2", title: "问卷2", isPublished: false },{ id: "q3", title: "问卷3", isPublished: true },{ id: "q4", title: "问卷4", isPublished: false },{ id: "q5", title: "问卷5", isPublished: true },];const [questionList, setQuestionList] = useState(questions);const r = Math.random().toString().slice(-3);// 新增问卷function add() {setQuestionList([...questionList,{id: "q" + r,title: "问卷" + r,isPublished: false,},]);}// 删除问卷function deleteQuestion(id: string) {setQuestionList(questionList.filter((q) => {return q.id !== id;}));}return (<div><h1>问卷列表页</h1><div>{questionList.map((q) => {const { id, title, isPublished } = q;return (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}deleteQuestion={deleteQuestion}/>);})}</div><div><button onClick={add}>新增问卷</button></div></div>); };export default List2;
QuestionCard.tsx代码如下:
import React, { FC } from "react";import "./QuestionCard.css";type PropsType = {id: string;title: string;isPublished: boolean;deleteQuestion: (id: string) => void; };const QuestionCard: FC<PropsType> = (props) => {const { id, title, isPublished, deleteQuestion } = props;//编辑问卷function edit(id: string) {console.log("id:", id);}// 删除问卷function del(id: string) {deleteQuestion(id);}return (<div key={id} className="list-item"><strong>{title}</strong> {/* 条件判断 */}{isPublished ? (<span style={{ color: "green" }}>已发布</span>) : (<span>未发布 </span>)} <button onClick={() => edit(id)}>编辑问卷</button> <button onClick={() => del(id)}>删除问卷</button></div>); };export default QuestionCard;
页面效果如下图所示:
-
发布问卷
List2.tsx代码如下:
import { FC, useState } from "react";import QuestionCard from "./components/QuestionCard";
const List2: FC = () => {//列表页//问卷列表数据const questions = [{ id: "q1", title: "问卷1", isPublished: false },{ id: "q2", title: "问卷2", isPublished: false },{ id: "q3", title: "问卷3", isPublished: true },{ id: "q4", title: "问卷4", isPublished: false },{ id: "q5", title: "问卷5", isPublished: true },];const [questionList, setQuestionList] = useState(questions);const r = Math.random().toString().slice(-3);// 新增问卷function add() {setQuestionList([...questionList,{id: "q" + r,title: "问卷" + r,isPublished: false,},]);}// 删除问卷function deleteQuestion(id: string) {setQuestionList(questionList.filter((q) => {return q.id !== id;}));}// 删除问卷function publishQuestion(id: string) {setQuestionList(questionList.map((q) => {if (q.id !== id) {return q;}return {...q,isPublished: true,};}));}return (<div><h1>问卷列表页</h1><div>{questionList.map((q) => {const { id, title, isPublished } = q;return (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}deleteQuestion={deleteQuestion}publishQuestion={publishQuestion}/>);})}</div><div><button onClick={add}>新增问卷</button></div></div>);
};export default List2;
QuestionCard.tsx代码如下:
import React, { FC } from "react";import "./QuestionCard.css";type PropsType = {id: string;title: string;isPublished: boolean;deleteQuestion?: (id: string) => void;publishQuestion?: (id: string) => void;
};const QuestionCard: FC<PropsType> = (props) => {const { id, title, isPublished, deleteQuestion, publishQuestion } = props;//编辑问卷function edit(id: string) {console.log("id:", id);}// 删除问卷function del(id: string) {deleteQuestion && deleteQuestion(id);}// 发布问卷function publish(id: string) {publishQuestion && publishQuestion(id);}return (<div key={id} className="list-item"><strong>{title}</strong> {/* 条件判断 */}{isPublished ? (<span style={{ color: "green" }}>已发布</span>) : (<span>未发布 </span>)} <button onClick={() => edit(id)}>编辑问卷</button> <button onClick={() => del(id)}>删除问卷</button> <button onClick={() => publish(id)}>发布问卷</button></div>);
};export default QuestionCard;
4 immer
如果你的 state 有多层的嵌套,你或许应该考虑 将其扁平化。但是,如果你不想改变 state 的数据结构,你可能更喜欢用一种更便捷的方式来实现嵌套展开的效果。Immer 是一个非常流行的库,它可以让你使用简便但可以直接修改的语法编写代码,并会帮你处理好复制的过程。通过使用 Immer,你写出的代码看起来就像是你“打破了规则”而直接修改了对象:
immer的使用说明参考下面连接2,这里用immer重构问卷,List2.tsx代码如下:
import { FC, useState } from "react";
import { useImmer } from "use-immer";import QuestionCard from "./components/QuestionCard";
const List2: FC = () => {//列表页//问卷列表数据const questions = [{ id: "q1", title: "问卷1", isPublished: false },{ id: "q2", title: "问卷2", isPublished: false },{ id: "q3", title: "问卷3", isPublished: true },{ id: "q4", title: "问卷4", isPublished: false },{ id: "q5", title: "问卷5", isPublished: true },];const [questionList, updateQuestionList] = useImmer(questions);const r = Math.random().toString().slice(-3);// 新增问卷function add() {updateQuestionList((draft) => {draft.push({id: "q" + r,title: "问卷" + r,isPublished: false,});});}// 删除问卷function deleteQuestion(id: string) {updateQuestionList((draft) => {const index = draft.findIndex((q) => q.id === id);draft.splice(index, 1);});}// 发布问卷function publishQuestion(id: string) {updateQuestionList((draft) => {const q = draft.find((q) => q.id === id);q && (q.isPublished = true);});}return (<div><h1>问卷列表页</h1><div>{questionList.map((q) => {const { id, title, isPublished } = q;return (<QuestionCardkey={id}id={id}title={title}isPublished={isPublished}deleteQuestion={deleteQuestion}publishQuestion={publishQuestion}/>);})}</div><div><button onClick={add}>新增问卷</button></div></div>);
};export default List2;
结语
❓QQ:806797785
⭐️仓库地址:https://gitee.com/gaogzhen
⭐️仓库地址:https://github.com/gaogzhen
[1]useState[CP/OL].
[2]使用 Immer 编写简洁的更新逻辑[CP/OL].