文章目錄
- 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].