💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 概述 State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。 ## state ``` // 一个用来存储变量,一个用来设置变量 let [count, setCount] = useState(0); ``` ### 常规示例 ``` import { useState, useEffect } from "react"; export default function Demo() { let [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return ( <div> <button onClick={handleClick}>Click me</button> <p>Count: {count}</p> </div> ); } ``` **设置多个state 只会为下一次渲染变更 state 的值** ``` <> <h1>{number}</h1> <button onClick={() => { setNumber(number + 1); setNumber(number + 1); setNumber(number + 1); }}>+3</button> </> ) ``` >**虽然触发了三次,但是number 触发一次,因为每次触发时,number都是在0的状态触发** 等价 ``` setNumber(0+ 1); setNumber(0+ 1); setNumber(0+ 1); alert(number) //0 setTimeout(() => { // 哪怕使用 settimeout, number也是0 alert(number); }, 3000); ``` ### 多次更新state,使用匿名函数 ``` setNumber(n => n + 1); setNumber(n => n + 1); setNumber(n => n + 1); ``` ### 更新对象 ``` // 声明 const [person, setPerson] = useState({ firstName: 'Barbara', lastName: 'Hepworth', email: 'bhepworth@sculpture.com' }); // 更新 setPerson({ firstName: e.target.value, // 从 input 中获取新的 first name lastName: person.lastName, email: person.email }); // 更新某个值 const [person, setPerson] = useState({ ...person, email: 'bhepworth@sculpture.com' }); ``` > 以上示例只是浅拷贝 #### 使用深拷贝 ``` const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); // 方式一: 创建一个新对象 const nextArtwork = { ...person.artwork, city: 'New Delhi' }; const nextPerson = { ...person, artwork: nextArtwork }; setPerson(nextPerson); // 方式二: 使用展开,推荐 setPerson({ ...person, // 复制其它字段的数据 artwork: { // 替换 artwork 字段 ...person.artwork, // 复制之前 person.artwork 中的数据 city: 'New Delhi' // 但是将 city 的值替换为 New Delhi! } }); // ``` ### 更新数组 | 避免使用 (会改变原始数组) | 推荐使用 (会返回一个新数组) | | --- | --- | | 添加元素 | `push`,`unshift` | `concat`,`[...arr]`展开语法([例子](https://zh-hans.react.dev/learn/updating-arrays-in-state#adding-to-an-array)) | | 删除元素 | `pop`,`shift`,`splice` | `filter`,`slice`([例子](https://zh-hans.react.dev/learn/updating-arrays-in-state#removing-from-an-array)) | | 替换元素 | `splice`,`arr[i] = ...`赋值 | `map`([例子](https://zh-hans.react.dev/learn/updating-arrays-in-state#replacing-items-in-an-array)) | | 排序 | `reverse`,`sort` | 先将数组复制一份([例子](https://zh-hans.react.dev/learn/updating-arrays-in-state#making-other-changes-to-an-array)) | > 或者使用 immer ,可使用所有的方法 bad ``` const [artists, setArtists] = useState([]); <button onClick={() => { artists.push({ id: nextId++, name: name, }); }}>添加</button> ``` good ``` setArtists( // 替换 state [ // 是通过传入一个新数组实现的 ...artists, // 新数组包含原数组的所有元素 { id: nextId++, name: name } // 并在末尾添加了一个新的元素 ] ); // 追加到之前 setArtists([ { id: nextId++, name: name }, ...artists // 将原数组中的元素放在末尾 ]); // 删除元素 setArtists( artists.filter(a => a.id !== artist.id ) ); ``` ### 在相同位置重置 state **方式一: 不同位置** 由于react ,会根据组件在树中的不同位置,判断是否保留 state ``` export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA && <Counter person="Taylor" /> } {!isPlayerA && <Counter person="Sarah" /> } <button onClick={() => { setIsPlayerA(!isPlayerA); }}> 下一位玩家! </button> </div> ); } ``` **方式二:指定key** 请记住 key 不是全局唯一的。它们只能指定 父组件内部 的顺序。 ``` export default function Scoreboard() { const [isPlayerA, setIsPlayerA] = useState(true); return ( <div> {isPlayerA ? ( <Counter key="Taylor" person="Taylor" /> ) : ( <Counter key="Sarah" person="Sarah" /> )} <button onClick={() => { setIsPlayerA(!isPlayerA); }}> 下一位玩家! </button> </div> ); } ``` 指定key 的第二个示例 ``` export default function App() { const [showHint, setShowHint] = useState(false); if (showHint) { return ( <div> <p><i>提示:你最喜欢的城市?</i></p> <Form key='123' /> <button onClick={() => { setShowHint(false); }}>隐藏提示</button> </div> ); } return ( <div> <Form key='123' /> <button onClick={() => { setShowHint(true); }}>显示提示</button> </div> ); } ``` ## reducer 整个state状态 示例 ``` import { useReducer } from 'react'; type item = { id:number, text?:string, done?:boolean } type action = { type:string, task:item } function tasksReducer(tasks:item[],action:action):item[]{ switch(action.type){ case 'added':{ return [...tasks,action.task] } case 'update':{ return tasks.map((t:item)=>{ if(t.id===action.task.id){ return action.task } return t }) } case 'deleted':{ return tasks.filter((t:item)=>t.id!==action.task.id) } } return tasks } export default function Demo11() { const data:item[] = []; const [tasks, dispatch] = useReducer(tasksReducer, data); function handleAdd(task:item){ dispatch({ type:'added', task:task }) } function handleUpdate(task:item){ dispatch({ type:'update', task:task }) } function handleDelete(task:item){ dispatch({ type:'deleted', task:task }) } return <div> <p>{JSON.stringify(tasks)}</p> <button onClick={()=>handleAdd({id:1,text:'111',done:true})}>button1</button> <button onClick={()=>handleUpdate({id:1,text:'222',done:false})}>button2</button> <button onClick={()=>handleDelete({id:1})}>button3</button> </div> } ``` > 使用 Immer 简化 reducer