React自学 基础一
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;}));
};