0302useState-hooks-react-仿低代码平台项目
文章目录
- 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 ( <QuestionCard key={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 ( <QuestionCard key={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 ( <QuestionCard key={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 (
<QuestionCard
key={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 (
<QuestionCard
key={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].