React基礎
React 是一個由 Facebook(現 Meta)開發并維護的、開源的 JavaScript 庫,主要用于 構建用戶界面(UI),尤其是單頁面應用程序中的動態、交互式界面。
簡單示例:
import React, { useState } from 'react';function Counter() {const [count, setCount] = useState(0); // 使用 React Hooks 管理狀態return (<div><p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>Click me</button></div>);
}
一、創建React工程
創建 React 項目主要有三種主流方式,根據你的需求(如開發效率、性能要求、是否需要服務端渲染等),可靈活選擇以下任一方法:
🚀 一、使用 Vite(推薦:速度快、現代化)
Vite 是當前最受歡迎的 React 項目初始化工具,啟動速度和熱更新遠超傳統工具,適合絕大多數新項目。
操作步驟:
-
確保已安裝 Node.js(建議 v16+)。
-
在終端執行以下命令:
npm create vite@latest my-vite-app -- --template react
-
進入項目目錄并安裝依賴:
cd my-vite-app npm install
-
啟動開發服務器:
npm run dev
- 訪問
http://localhost:5173
查看項目。
- 訪問
-
在Chrome瀏覽器中添加組件
React 因其高效、靈活和強大的社區支持,成為現代前端開發的主流選擇之一。
在Chrome瀏覽器中添加組件
?? 二、使用 Next.js(適合服務端渲染/靜態站點)
若項目需服務端渲染(SSR)、靜態生成(SSG)或強 SEO 支持(如博客、電商),Next.js 是最佳選擇。
操作步驟:
- 創建項目:
npx create-next-app@latest my-next-app
- 進入目錄并啟動:
cd my-next-app npm run dev
- 訪問
http://localhost:3000
。
- 訪問
🧩 三、使用 create-react-app(傳統方式,已逐漸淘汰)
雖然官方仍在維護,但啟動慢、配置隱藏,僅適合學習或兼容舊項目。
操作步驟:
- 全局安裝(可選):
npm install -g create-react-app
- 創建項目:
npx create-react-app my-app
- 啟動項目:
cd my-app npm start
- 訪問
http://localhost:3000
。
- 訪問
📊 三種方式對比與適用場景
工具 | 優勢 | 適用場景 | 啟動命令 |
---|---|---|---|
Vite | ? 超快啟動、輕量、配置靈活 | 新項目、開發效率優先 | npm run dev |
Next.js | 🌐 服務端渲染、SEO 友好、全棧能力 | 博客、電商、需 SEO 的應用 | npm run dev |
CRA | 🧠 零配置、官方維護(但慢) | 學習 React 基礎 | npm start |
? 常見問題解答(FAQ)
- Q1:是否需要全局安裝工具?
不需要!現代工具(如 Vite、Next.js)通過npx
直接運行最新版本,避免全局依賴沖突。 - Q2:項目名稱能否用大寫字母?
不能!所有工具均要求項目名全小寫(如my-app
)。 - Q3:如何修改默認端口?
在package.json
的啟動命令后添加--port=新端口號
,例如:npm run dev -- --port=4000
💎 總結建議
- 新手/一般項目 → 選 Vite,體驗流暢開發;
- 內容型網站/SEO 關鍵 → 選 Next.js,直接支持服務端渲染;
- 學習 React 基礎 → 可嘗試
create-react-app
,但注意其性能局限。
提示:以上命令均需在終端(如 VS Code 終端、命令行)執行。遇到依賴問題可嘗試
npm install --force
強制重裝。
二、JSX
2.1 關于 JSX
JSX(JavaScript XML) 是一種 JavaScript 的語法擴展(解析工具進行特定解析),由 Facebook 團隊為 React 框架設計。它的核心作用是將 HTML 結構直接嵌入 JavaScript 代碼中,實現聲明式的 UI 構建。
核心特點:
-
類 HTML 語法:
允許在 JavaScript 中編寫類似 HTML 的標簽(例如<div>Hello</div>
)。 -
JavaScript 表達式嵌入:
用{ }
包裹動態內容(如變量、函數調用):const name = "Alice"; const element = <h1>Hello, {name}</h1>; // 動態渲染
-
編譯為 JavaScript:
JSX 本身不能被瀏覽器直接執行,需要通過工具(如 Babel)編譯為標準 JavaScript:// 編譯前 (JSX) <button className="btn">Click</button>// 編譯后 (JavaScript) React.createElement("button", { className: "btn" }, "Click");
-
與 React 深度集成:
主要用于構建 React 組件的 UI 結構,但也可用于其他庫(如 Vue 的 JSX 支持)。
2.2 關于 Babel (https://babeljs.io/)
Babel 是一個 JavaScript 編譯器,它的核心功能是將新版本的 JavaScript 代碼(或 JSX 等擴展語法)轉換為向后兼容的舊版本 JavaScript,確保代碼能在所有瀏覽器中運行。
關鍵作用:
- 編譯 JSX:
將 JSX 語法轉換為瀏覽器可理解的React.createElement()
調用(如上例所示)。 - 轉譯現代 JavaScript:
將 ES6+ 代碼(如箭頭函數、async/await
、類)轉換為 ES5 等舊標準。// 編譯前 (ES6) const list = items.map(item => <li key={item.id}>{item.name}</li>);// 編譯后 (ES5) var list = items.map(function(item) {return React.createElement("li", { key: item.id }, item.name); });
- 插件化架構:
通過插件支持實驗性語法(如裝飾器)、類型檢查(TypeScript)、代碼優化等。
2.3 JSX 與 Babel 的關系
- 依賴 Babel 處理:
JSX 必須通過 Babel(或類似工具)編譯為純 JavaScript 才能運行。 - 常用預設:
在 React 項目中,通常使用 Babel 預設插件@babel/preset-react
來編譯 JSX。 - 工作流程示例:
2.4 如何使用?
- 安裝 Babel:
npm install @babel/core @babel/preset-react
- 配置 Babel(創建
.babelrc
文件):{"presets": ["@babel/preset-react"] }
- 與構建工具集成:
配合 Webpack/Rollup 等工具自動完成編譯(例如使用babel-loader
)。
? 總結:
- JSX 是描述 UI 的語法糖,需編譯為 JavaScript。
- Babel 是編譯工具鏈的核心,處理 JSX 和現代 JavaScript 的兼容性問題。
官網 babeljs.io 提供了實時編譯演示、插件文檔和配置指南。
二(支線)、JSX 中使用 JavaScript 表達式
在 JSX 中,你可以使用大括號 {}
來嵌入 JavaScript 表達式。這允許你在 JSX 中動態地插入值、執行計算和渲染邏輯。
2.1 基本用法
const name = 'John Doe';
const element = <h1>Hello, {name}</h1>;
2.2 表達式類型
-
變量引用
const user = { firstName: 'Jane', lastName: 'Doe' }; const element = <p>{user.firstName} {user.lastName}</p>;
-
函數調用
function formatName(user) {return `${user.firstName} ${user.lastName}`;}const element = <h2>{formatName(user)}</h2>;
- 算術運算
const total = 10;const count = 3;const element = <p>Remaining: {total - count}</p>;
-
三元表達式
const isLoggedIn = true; const element = (<div>{isLoggedIn ? 'Welcome back!' : 'Please sign in'}</div> );
-
數組方法(列表渲染)
const numbers = [1, 2, 3, 4, 5];const listItems = numbers.map((number) => <li key={number}>{number}</li>);
2.3 JSX 中的列表渲染示例
列表渲染是 React 中常見的操作,通常使用 map()
方法來遍歷數組并生成一組 JSX 元素。下面提供多個不同場景的列表渲染示例:
2.3.1 基礎列表渲染
const fruits = ['Apple', 'Banana', 'Orange'];function FruitList() {return (<ul>{fruits.map((fruit, index) => (<li key={index}>{fruit}</li>))}</ul>);
}
2.3.2 對象數組渲染
const users = [{ id: 1, name: 'Alice', age: 25 },{ id: 2, name: 'Bob', age: 30 },{ id: 3, name: 'Charlie', age: 35 }
];function UserTable() {return (<table><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody>{users.map(user => (<tr key={user.id}><td>{user.name}</td><td>{user.age}</td></tr>))}</tbody></table>);
}
2.3.3 帶條件的列表渲染
在 JSX 中,我們經常需要根據條件來決定是否渲染某些內容。JavaScript 的邏輯與 (&&
) 運算符和三元運算符 (?:
) 是兩種常用的條件渲染方式。
1. 邏輯與 (&&
) 運算符
{condition && <Component />}
當 condition
為真時,渲染 <Component />
;否則不渲染任何內容。
示例
function Greeting({ isLoggedIn }) {return (<div>{isLoggedIn && <h1>Welcome back!</h1>}</div>);
}// 使用
<Greeting isLoggedIn={true} /> // 顯示 "Welcome back!"
<Greeting isLoggedIn={false} /> // 不顯示任何內容
特點
- 簡潔,適合簡單的條件渲染
- 當條件為
false
時,React 會跳過渲染(不會渲染null
或false
) - 注意:如果
condition
是0
這樣的 falsy 值,會渲染0
而不是跳過
2. 三元運算符 (?:
)
{condition ? <ComponentA /> : <ComponentB />}
當 condition
為真時,渲染 <ComponentA />
;否則渲染 <ComponentB />
。
示例
function Greeting({ isLoggedIn }) {return (<div>{isLoggedIn ? (<h1>Welcome back!</h1>) : (<h1>Please sign in.</h1>)}</div>);
}// 使用
<Greeting isLoggedIn={true} /> // 顯示 "Welcome back!"
<Greeting isLoggedIn={false} /> // 顯示 "Please sign in."
特點
- 適合需要兩種不同渲染結果的場景
- 更明確地表達兩種可能性
- 可以嵌套使用(但不推薦過于復雜的嵌套)
3. 在 JSX 外部使用 if
語句
提前決定要渲染的內容
function Greeting({ isLoggedIn }) {let content;if (isLoggedIn) {content = <h1>Welcome back!</h1>;} else {content = <h1>Please sign in.</h1>;}return <div>{content}</div>;
}
function renderContent(isLoggedIn) {if (isLoggedIn) {return <h1>Welcome back!</h1>;}return <h1>Please sign in.</h1>;
}function Greeting({ isLoggedIn }) {return <div>{renderContent(isLoggedIn)}</div>;
}
4. 立即執行函數來包含 if
邏輯
function Greeting({ isLoggedIn }) {return (<div>{(() => {if (isLoggedIn) {return <h1>Welcome back!</h1>;} else {return <h1>Please sign in.</h1>;}})()}</div>);
}
5.使用 switch
語句
function StatusIndicator({ status }) {let indicator;switch (status) {case 'loading':indicator = <Spinner />;break;case 'success':indicator = <Checkmark />;break;case 'error':indicator = <ErrorIcon />;break;default:indicator = null;}return <div>{indicator}</div>;
}
6.比較
方法 | 適用場景 | 優點 | 缺點 |
---|---|---|---|
外部 if | 復雜條件邏輯 | 清晰易讀 | 需要額外變量 |
IIFE | 簡單內聯條件 | 保持內聯 | 語法稍復雜 |
提取函數 | 可復用的條件渲染 | 可復用,易于測試 | 需要跳轉查看邏輯 |
switch | 多個互斥條件 | 結構清晰 | 代碼量較多 |
&& 運算符 | 單一條件,渲染或不渲染 | 簡潔 | 只能處理單一條件 |
三元運算符 | 二選一渲染 | 表達式形式,內聯 | 嵌套時難以閱讀 |
最佳實踐建議
處理 false 值的注意事項
// 當 count 為 0 時,&& 會渲染 0
{count && <Message count={count} />}// 修復方法:明確布爾值轉換
{count > 0 && <Message count={count} />}
{!!count && <Message count={count} />}
- 簡單條件:使用
&&
更簡潔 - 二選一渲染:使用三元運算符
- 避免復雜嵌套:如果條件太復雜,考慮提取為函數或組件
- 保持可讀性:復雜的條件邏輯可以拆分成多個變量
選擇哪種方式主要取決于具體場景和個人/團隊的編碼風格偏好,重要的是保持代碼的一致性和可讀性。
const products = [{ id: 1, name: 'Laptop', inStock: true },{ id: 2, name: 'Phone', inStock: false },{ id: 3, name: 'Tablet', inStock: true }
];function ProductList() {return (<ul>{products.map(product => (product.inStock && (<li key={product.id}>{product.name} - {product.inStock ? 'In Stock' : 'Out of Stock'}</li>)))}</ul>);
}
2.3.4 嵌套列表渲染
const departments = [{id: 1,name: 'Development',employees: ['John', 'Jane', 'Mike']},{id: 2,name: 'Design',employees: ['Sarah', 'Tom']}
];function DepartmentList() {return (<div>{departments.map(dept => (<div key={dept.id}><h3>{dept.name}</h3><ul>{dept.employees.map((employee, index) => (<li key={index}>{employee}</li>))}</ul></div>))}</div>);
}
2.3.5 使用組件渲染列表項
function TodoItem({ todo }) {return (<li><input type="checkbox" checked={todo.completed} /><span>{todo.text}</span></li>);
}function TodoList() {const todos = [{ id: 1, text: 'Learn React', completed: true },{ id: 2, text: 'Build a project', completed: false },{ id: 3, text: 'Deploy to production', completed: false }];return (<ul>{todos.map(todo => (<TodoItem key={todo.id} todo={todo} />))}</ul>);
}
2.3.6 重要注意事項
-
key 屬性:列表中的每個元素都應該有一個唯一的
key
屬性,通常使用數據中的 ID,// 好 {items.map(item => <li key={item.id}>{item.name}</li>)}// 如果沒有 ID,可以使用索引(但不推薦用于動態列表) {items.map((item, index) => <li key={index}>{item.name}</li>)}
-
避免在渲染時修改數據:不要在
map
內部修改原始數組 -
性能優化:對于大型列表,考慮使用虛擬滾動技術(如
react-window
庫)
2.4 注意事項
- JSX 中的表達式必須是返回值的表達式,不能使用語句(如
if
語句) - 對于條件渲染,可以使用三元運算符或邏輯與 (
&&
) 運算符 - 對象不能直接作為子元素渲染,需要轉換為字符串或提取特定屬性
// 錯誤示例
const user = { name: 'John' };
const element = <div>{user}</div>; // 錯誤// 正確示例
const element = <div>{user.name}</div>; // 正確
const element = <div>{JSON.stringify(user)}</div>; // 正確
JSX 中的 JavaScript 表達式使得動態 UI 的創建變得簡單而強大。
三、事件綁定+組件
3.1 React事件綁定
在 JSX 中綁定 React 事件與在 HTML 中綁定 DOM 事件類似,但有一些關鍵區別。以下是 React 事件綁定的主要特點和使用方法:
3.1.1 基本語法
<button onClick={handleClick}>點擊我
</button>
(1)與 HTML 事件的區別
- 命名采用駝峰式:
onclick
→onClick
,onchange
→onChange
- 傳入函數而不是字符串:
onclick="handleClick()"
→onClick={handleClick}
- 默認行為阻止方式不同:不能通過返回
false
來阻止默認行為
(2)常見事件類型
onClick
- 點擊事件onChange
- 表單元素變化onSubmit
- 表單提交onMouseEnter
- 鼠標進入onMouseLeave
- 鼠標離開onKeyDown
- 鍵盤按下onFocus
- 獲取焦點onBlur
- 失去焦點
3.1.2【1】事件處理函數
function handleClick(event) {// event 是合成事件(SyntheticEvent)console.log('按鈕被點擊了', event);
}function App() {return <button onClick={handleClick}>點擊</button>;
}
3.1.2 【2】 傳遞參數
(1)如果需要傳遞額外參數給事件處理函數
function handleClick(id, event) {console.log('ID:', id, '事件:', event);
}function App() {return (<button onClick={(e) => handleClick(123, e)}>帶參數點擊</button>);
}
🔍 特點分析:
- 事件對象正確傳遞:
onClick={(e) => handleClick(123, e)}
中的e
是 React 事件對象,被顯式傳遞給了handleClick
函數。 - 函數簽名匹配:
handleClick(id, event)
的兩個參數(id
和event
)都得到了正確的值。 - 用途場景:當需要訪問事件對象(如阻止默認行為、獲取事件目標等)時,這種方式是推薦的。
(2)箭頭函數的
不能直接寫函數調用,這事件綁定需要一個函數引用
function handleClick(event) {console.log('事件:', event);
}const App = () => {return (<button onClick={() => handleClick(123)}>帶參數點擊</button>);
};
🔍 問題分析:
- 事件對象未傳遞:
onClick={() => handleClick(123)}
中的handleClick
僅接收123
作為參數,事件對象(event
)沒有被傳入。 - 函數簽名不匹配:
handleClick(event)
期望的是一個event
參數,但實際傳入的是123
,因此event
參數會是undefined
。 - 潛在錯誤:如果
handleClick
中依賴event
的值(如event.target.value
、event.preventDefault()
等),會導致運行時錯誤或行為異常。
3.1.3 類組件中的綁定
在類組件中,通常需要在構造函數中綁定 this
或使用箭頭函數:
class MyComponent extends React.Component {constructor(props) {super(props);this.handleClick = this.handleClick.bind(this);}handleClick() {console.log('this:', this);}// 或者使用箭頭函數自動綁定 thishandleClick = () => {console.log('this:', this);}render() {return <button onClick={this.handleClick}>點擊</button>;}
}
3.1.4阻止默認行為和事件冒泡
function handleClick(event) {event.preventDefault(); // 阻止默認行為event.stopPropagation(); // 阻止事件冒泡console.log('事件處理');
}
3.1.5 合成事件(SyntheticEvent)
React 的事件是合成事件,是對原生事件的跨瀏覽器包裝,具有與原生事件相同的接口,包括:
event.preventDefault()
event.stopPropagation()
event.target
event.currentTarget
3.1.6 注意事項
- 事件處理函數中的
this
默認不是組件實例,需要手動綁定 - 避免在 render 方法中創建新函數,可能導致不必要的重新渲染
- React 17+ 中事件委托不再附加到 document,而是附加到 React 渲染的根 DOM 容器
正確的事件綁定方式可以幫助你編寫更高效、更易維護的 React 組件。
3.2 React組件
3.2.1 基礎使用
React 組件是構建用戶界面的獨立、可復用的代碼單元,將 UI 拆分為獨立模塊。每個組件管理自身的狀態和邏輯,通過組合形成復雜界面。
/*原生寫法*/
function Button (){return <button>Button</button>
}/*或者使用箭頭函數的寫法*/
const Button = () =>{return <button>Button</button>
}
使用上
/*自閉和*/
<Button/>
/*成對標簽*/
<Button/><Button/>
3.2.2 基礎樣式控制
在 React 中,控制組件樣式有多種基礎方式,每種方式各有適用場景。以下是主要方法及代碼示例:
1. 內聯樣式 (Inline Styles)
直接在 JSX 元素中使用 style
屬性,傳入樣式對象(屬性名需駝峰命名)
function Button() {return (<button style={{padding: "10px 20px",backgroundColor: "blue",color: "white",borderRadius: "4px",cursor: "pointer"}}>點擊我</button>);
}
特點:
- ? 簡單直接,適合動態樣式
- ? 無法使用偽類(:hover)和媒體查詢
- ? 性能不如 CSS 類(大量使用可能影響渲染)
2. 外部 CSS 文件
傳統 CSS 引入方式(最常用)
/* Button.css */
.my-button {padding: 10px 20px;background-color: blue;color: white;border-radius: 4px;cursor: pointer;
}.my-button:hover {background-color: darkblue; /* 支持偽類 */
}
組件中引入:
import "./Button.css";function Button() {return <button className="my-button">點擊我</button>;
}
特點:
- ? 支持所有 CSS 特性
- ? 瀏覽器緩存優化
- ? 全局作用域(需注意類名沖突)
3. CSS Modules(推薦)
自動局部作用域(文件命名:[name].module.css
)
/* Button.module.css */
.button {padding: 10px 20px;background: blue;
}
組件中使用:
import styles from "./Button.module.css";function Button() {return <button className={styles.button}>點擊我</button>;
}
編譯后類名會變成唯一值(如 Button_button__1H2k5
)
特點:
- ? 避免類名沖突
- ? 支持所有 CSS 特性
- ? 與普通 CSS 寫法一致
4. CSS-in-JS 庫 (如 styled-components)
通過 JavaScript 編寫樣式(需安裝庫)
npm install styled-components
import styled from "styled-components";// 創建帶樣式的組件
const StyledButton = styled.button`padding: 10px 20px;background: ${props => props.primary ? "blue" : "gray"};color: white;border-radius: 4px;&:hover {background: darkblue; /* 支持偽類 */}
`;function App() {return (<><StyledButton>普通按鈕</StyledButton><StyledButton primary>主要按鈕</StyledButton></>);
}
特點:
- ? 動態樣式支持最佳
- ? 自動處理作用域
- ? 增加包體積
- ? 學習曲線較陡
5. 實用類庫 (Tailwind CSS)
原子化 CSS 方案(需配置)
function Button() {return (<button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">點擊我</button>);
}
特點:
- ? 快速原型開發
- ? 響應式設計便捷
- ? 需學習類名系統
- ? HTML 可能臃腫
選擇建議:
場景 | 推薦方式 |
---|---|
簡單原型/少量動態樣式 | 內聯樣式 |
傳統項目/團隊熟悉 CSS | 外部 CSS + BEM |
組件化/避免類名沖突 | CSS Modules |
復雜動態樣式/主題系統 | CSS-in-JS |
快速開發/統一設計系統 | Tailwind CSS |
最佳實踐提示:
- 大型項目推薦 CSS Modules 或 CSS-in-JS
- 避免過度使用內聯樣式(性能考慮)
- 動態樣式可結合:
className
條件拼接 或 CSS 變量- 使用 :where() 降低 CSS 特異性(如
:where(.card) .title
)
四、useState
useState
是 React 中用于聲明組件狀態的一個 Hook,它讓函數組件能夠擁有自己的內部狀態。(向組件添加一個狀態變量,一旦變化UI也隨之變化**(數據驅動視圖**))
通過 useState
,你可以在組件內部保存數據并在數據發生變化時重新渲染組件。
4.1 useState 基礎結構
const [state, setState] = useState(initialValue);
- state:當前狀態值(只讀)
- setState:更新狀態的函數
- initialValue:狀態的初始值(可以是任何類型)
4.2 狀態不可變性原則詳解
4.2.1 為什么狀態必須不可變?
- 變更檢測:React 通過引用比較檢測狀態變化
- 性能優化:避免不必要的重新渲染
- 可預測性:狀態變更路徑清晰可追蹤
- 時間旅行調試:支持狀態歷史記錄和回滾
4.2.2 錯誤 vs 正確更新方式對比
必須創建狀態的新副本進行替換,而不是直接修改現有狀態。
狀態類型 | 錯誤方式(直接修改) | 正確方式(創建新值) |
---|---|---|
對象 | user.age = 31; setUser(user); | setUser({...user, age: 31}); |
數組 | items.push('new'); setItems(items); | setItems([...items, 'new']); |
嵌套對象 | user.address.city = 'Paris'; | setUser({...user, address: {...user.address, city: 'Paris'}}); |
4.3 待處理:狀態管理實戰模式
1. 表單狀態處理
function UserForm() {const [user, setUser] = useState({name: '',email: '',preferences: { theme: 'light', notifications: true }});const handleChange = (field, value) => {setUser(prev => ({...prev,[field]: value}));};const toggleTheme = () => {setUser(prev => ({...prev,preferences: {...prev.preferences,theme: prev.preferences.theme === 'light' ? 'dark' : 'light'}}));};return (<form><input value={user.name} onChange={e => handleChange('name', e.target.value)} /><input value={user.email} onChange={e => handleChange('email', e.target.value)} /><button type="button" onClick={toggleTheme}>切換主題(當前:{user.preferences.theme})</button></form>);
}
2. 列表狀態管理
function TodoList() {const [todos, setTodos] = useState([{ id: 1, text: '學習React', completed: false }]);// 添加新任務(創建新數組)const addTodo = text => {setTodos(prev => [...prev,{ id: Date.now(), text, completed: false }]);};// 切換任務狀態(創建新對象)const toggleTodo = id => {setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo));};// 刪除任務(創建過濾后的新數組)const deleteTodo = id => {setTodos(prev => prev.filter(todo => todo.id !== id));};return (<div><ul>{todos.map(todo => (<li key={todo.id}><input type="checkbox"checked={todo.completed}onChange={() => toggleTodo(todo.id)}/><span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>{todo.text}</span><button onClick={() => deleteTodo(todo.id)}>刪除</button></li>))}</ul></div>);
}
4.4 待處理:高級技巧與優化策略
1. 函數式更新
// 當新狀態依賴舊狀態時使用
setCount(prevCount => prevCount + 1);// 批量更新示例
const incrementTwice = () => {setCount(prev => prev + 1); // 使用前值計算setCount(prev => prev + 1); // 確保兩次更新都生效
};
2. 惰性初始化
// 復雜初始狀態的計算只執行一次
const [data, setData] = useState(() => {const expensiveValue = calculateExpensiveValue();return expensiveValue;
});
3. 狀態結構優化
4. 使用 Immer 簡化不可變更新
import produce from 'immer';const [user, setUser] = useState({name: 'Alice',profile: {level: 5,achievements: ['新手', '探索者']}
});// 使用 Immer 進行"可變式"更新(底層仍不可變)
const addAchievement = title => {setUser(produce(draft => {draft.profile.achievements.push(title);}));
};const updateLevel = () => {setUser(produce(draft => {draft.profile.level += 1;}));
};