- 初始化Docusaurus 3.9.2项目基础架构 - 添加React 19.0.0和完整依赖配置 - 创建项目规划文档 (PROJECT_PLAN.md) - 创建详细设计文档 (DETAILED_DESIGN.md) - 创建CLAUDE.md指导文档 - 保留原始Docusaurus模板内容 - 配置博客和文档系统 - 添加自定义组件和样式 技术栈: - Docusaurus 3.9.2 - React 19.0.0 - MDX支持 - 响应式设计 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
52 KiB
React学习中心 - 详细设计文档
📦 核心组件API设计
1. CodePlayground 组件(完整版)
组件概述
基于 @codesandbox/sandpack-react 的完整代码编辑器,支持多文件编辑、实时预览、代码保存。
API 设计
interface CodePlaygroundProps {
// 文件配置
files: Record<string, string>;
entry?: string;
// 主题配置
theme?: 'light' | 'dark' | 'auto';
customTheme?: SandpackTheme;
// 行为配置
showConsole?: boolean;
showTabs?: boolean;
showLineNumbers?: boolean;
showRefreshButton?: boolean;
readOnly?: boolean;
// 编辑器配置
editorHeight?: number;
editorWidthPercentage?: number;
// 高级配置
options?: SandpackConfig;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
// 事件回调
onCodeChange?: (code: string, path: string) => void;
onConsoleMessage?: (message: ConsoleMessage) => void;
onError?: (error: Error) => void;
}
使用示例
// 基础使用
<CodePlayground
files={{
'/App.js': `
import React from 'react';
export default function App() {
return <h1>Hello React!</h1>;
}
`,
'/index.js': `
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
`
}}
entry="/index.js"
theme="auto"
showConsole={true}
onCodeChange={(code, path) => {
console.log(`${path} changed:`, code);
}}
/>
// 高级配置示例
<CodePlayground
files={{...}}
options={{
recompileMode: 'delayed',
recompileDelay: 500,
editorHeight: 500,
wrapContent: true,
editorWidthPercentage: 55,
showTabs: true,
showLineNumbers: true,
closableTabs: false,
}}
dependencies={{
'lodash': '4.17.21',
'axios': '^1.3.0'
}}
/>
主题自定义
// 自定义主题配置
const customLightTheme = {
colors: {
surface1: '#ffffff',
surface2: '#f6f9fc',
surface3: '#eef2f7',
accent: '#0969da',
error: '#d1242f',
success: '#1a7f37'
},
syntax: {
plain: '#24292f',
comment: '#656d76',
keyword: '#cf222e',
string: '#0a3069',
number: '#953800',
boolean: '#cf222e'
}
};
2. LiveCode 组件(轻量版)
组件概述
基于 react-live 的轻量级实时代码预览,适合小代码片段的快速演示。
API 设计
interface LiveCodeProps {
// 代码内容
code: string;
scope?: Record<string, any>;
// UI配置
showEditor?: boolean;
showPreview?: boolean;
showError?: boolean;
language?: 'jsx' | 'javascript' | 'typescript';
// 主题
theme?: 'light' | 'dark';
// 行为
noInline?: boolean;
transformCode?: (code: string) => string;
// 事件
onError?: (error: Error) => void;
onSuccess?: () => void;
}
使用示例
// 基础使用 - useState示例
<LiveCode
code={`
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div style={{ padding: '20px' }}>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
`}
scope={{ React }}
/>
// 高级配置
<LiveCode
code={code}
scope={{ React, useState, useEffect, MyComponent }}
theme="dark"
showEditor={true}
showPreview={true}
showError={true}
transformCode={(code) => {
// 自动添加React导入
return code.includes('import')
? code
: `import React from 'react';\n${code}`;
}}
/>
3. LearningPath 组件
组件概述
可视化学习路径展示,显示技能树和学习进度。
API 设计
interface LearningPathNode {
id: string;
title: string;
description: string;
level: 'beginner' | 'intermediate' | 'advanced';
estimatedTime: string; // '30min', '1h', '2h'
prerequisites?: string[]; // 前置节点ID
path: string; // 文档路径
completed?: boolean;
locked?: boolean;
}
interface LearningPathProps {
nodes: LearningPathNode[];
onNodeClick?: (node: LearningPathNode) => void;
onNodeComplete?: (nodeId: string) => void;
initialExpanded?: string[];
showProgress?: boolean;
layout?: 'vertical' | 'horizontal' | 'grid';
}
使用示例
// 基础学习路径
const hooksPath = [
{
id: 'useState',
title: 'useState Hook',
description: '学习状态管理基础',
level: 'beginner' as const,
estimatedTime: '45min',
path: '/docs/hooks/basics/useState',
completed: false,
locked: false
},
{
id: 'useEffect',
title: 'useEffect Hook',
description: '学习副作用处理',
level: 'beginner' as const,
estimatedTime: '60min',
prerequisites: ['useState'],
path: '/docs/hooks/basics/useEffect',
completed: false,
locked: false
}
];
<LearningPath
nodes={hooksPath}
onNodeClick={(node) => navigate(node.path)}
layout="vertical"
showProgress={true}
/>
4. CopyButton 组件
API 设计
interface CopyButtonProps {
code: string;
fileName?: string;
showIcon?: boolean;
showText?: boolean;
tooltipPosition?: 'top' | 'bottom' | 'left' | 'right';
className?: string;
onCopy?: (success: boolean) => void;
}
使用示例
<CopyButton
code={sampleCode}
fileName="App.js"
showIcon={true}
showText={true}
tooltipPosition="top"
onCopy={(success) => {
if (success) {
toast.success('代码已复制到剪贴板');
}
}}
/>
📚 学习路径详细内容规划
模块1: 基础入门 (Foundations)
1.1 什么是React? (What is React)
文档路径: docs/foundations/what-is-react.mdx
预估时长: 30分钟
难度: 初级
内容大纲:
# 什么是React?
## 本章学习目标
- 理解React的核心概念
- 了解React的历史和设计哲学
- 掌握React的优势和适用场景
- 搭建第一个React应用
## 1. React简介
- React是由Facebook(现Meta)开发的前端框架
- 2011年首次应用于Facebook News Feed
- 2013年开源发布
- 当前版本:React 19(2025年)
## 2. 核心概念
- 组件化开发
- 声明式编程
- 虚拟DOM
- 单向数据流
## 3. React的优势
- 组件复用性
- 开发效率
- 生态系统
- 社区支持
- 性能优化
## 4. React vs 其他框架
- vs Vue.js
- vs Angular
- vs 原生JavaScript
## 交互式演示1:Hello React
<CodePlayground
files={{
'/App.js': `
import React from 'react';
function App() {
return (
<div style={{ padding: '20px', fontFamily: 'Arial' }}>
<h1>Hello React!</h1>
<p>Welcome to your first React component.</p>
</div>
);
}
export default App;
`
}}
/>
## 交互式演示2:组件化思维
<CodePlayground
files={{
'/components/Welcome.js': `
import React from 'react';
function Welcome({ name, age }) {
return (
<div style={{ border: '1px solid #ccc', padding: '10px' }}>
<h2>Hello, {name}!</h2>
<p>You are {age} years old.</p>
</div>
);
}
export default Welcome;
`,
'/App.js': `
import React from 'react';
import Welcome from './components/Welcome';
function App() {
return (
<div style={{ padding: '20px' }}>
<Welcome name="Alice" age={25} />
<Welcome name="Bob" age={30} />
</div>
);
}
export default App;
`
}}
/>
## 实践练习
### 练习1:创建你的第一个组件
使用Sandbox编辑器,创建一个PersonalCard组件,显示姓名、职业和爱好。
### 练习2:组件复用
修改PersonalCard组件,使其可以显示多个人的信息。
## 本章小结
- React是用于构建用户界面的JavaScript库
- 组件是React的核心概念
- React支持组件复用和模块化开发
## 延伸阅读
- [React官方文档](https://react.dev)
- [React历史](https://reactjs.org/blog/2013/06/05/why-react.html)
## 下一章
[JSX语法详解 →](jsx-syntax)
1.2 JSX语法详解 (JSX Syntax)
文档路径: docs/foundations/jsx-syntax.mdx
预估时长: 45分钟
难度: 初级
内容大纲:
# JSX语法详解
## 本章学习目标
- 理解JSX的作用和优势
- 掌握JSX语法规则
- 学会在JSX中嵌入表达式
- 掌握条件渲染和列表渲染
- 理解JSX的工作原理
## 1. 什么是JSX?
JSX是一种JavaScript的语法扩展,允许在JavaScript中编写类似HTML的代码。
### JSX示例
```jsx
const element = <h1>Hello, world!</h1>;
2. JSX基础语法
2.1 标签闭合
// 自闭合
<img src="logo.png" alt="Logo" />
// 配对闭合
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
2.2 嵌套规则
- 只能有一个根元素
- 使用Fragment(<></>)避免额外div
3. 嵌入表达式
3.1 基本用法
<LiveCode code={` function App() { const name = 'Alice'; const age = 25;
return (
Hello, {name}!
You are {age} years old.
Next year you will be {age + 1}
3.2 复杂表达式
<LiveCode code={` function App() { const isLoggedIn = true; const user = { name: 'Bob', role: 'Admin' };
return (
Welcome back, {user.name}!
:Please log in
} {user.role === 'Admin' && Delete}4. 属性和Props
4.1 字符串常量
<MyComponent message="Hello" />
4.2 JavaScript表达式
<MyComponent message={variable} />
<MyComponent count={10 + 20} />
<MyComponent isActive={true} />
4.3 特殊属性
classNameinstead ofclasshtmlForinstead offoronClickfor click eventsstyleaccepts objects
4.4 展开属性
const props = { message: 'Hello', count: 5 };
<MyComponent {...props} />
5. 条件渲染
5.1 if语句
<LiveCode code={` function App({ user }) { if (!user) { return
Loading...
; }return (
Welcome, {user.name}
5.2 三元运算符
<LiveCode
code={function App({ isAdmin }) { return ( <div> {isAdmin ? <AdminPanel /> : <UserPanel />} </div> ); }}
/>
5.3 逻辑与运算符
<LiveCode
code={function App({ notifications }) { return ( <div> <h1>Dashboard</h1> {notifications.length > 0 && ( <div className="notifications"> You have {notifications.length} new notifications </div> )} </div> ); }}
/>
6. 列表渲染
6.1 使用map方法
<LiveCode code={` function App() { const items = ['Apple', 'Banana', 'Cherry'];
return (
-
{items.map((item, index) => (
- {item} ))}
6.2 对象数组
<LiveCode code={` function App() { const users = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' } ];
return (
{user.name}
{user.email}
7. 事件处理
7.1 基础事件
<LiveCode code={` function App() { function handleClick() { alert('Button clicked!'); }
return ( Click Me ); } `} />
7.2 事件参数
<LiveCode
code={ function App() { function handleItemClick(item) { alert(\Clicked: ${item}`);
}
const items = ['React', 'Vue', 'Angular'];
return (
-
{items.map((item, index) => (
<li key={index} onClick={() => handleItemClick(item)}>
{item}
))}
8. 样式处理
8.1 内联样式
<LiveCode code={` function App() { const buttonStyle = { backgroundColor: '#007bff', color: 'white', padding: '10px 20px', border: 'none', borderRadius: '5px', cursor: 'pointer' };
return ( Styled Button ); } `} />
8.2 className
function App() {
return (
<div className="container">
<h1 className="title">Hello</h1>
</div>
);
}
实践练习
练习1:个人信息卡片
创建一个ProfileCard组件,显示姓名、年龄、职业,并使用不同的条件渲染。
练习2:待办事项列表
创建一个TodoList组件,支持添加、显示、标记完成功能。
练习3:购物车
创建一个ShoppingCart组件,显示商品列表、数量、价格和总价。
常见错误
1. 未使用key属性
// ❌ 错误
{items.map(item => <div>{item}</div>)}
// ✅ 正确
{items.map(item => <div key={item.id}>{item}</div>)}
2. 未闭合标签
// ❌ 错误
<img src="image.png">
// ✅ 正确
<img src="image.png" />
3. 错误使用class
// ❌ 错误
<div class="container">
// ✅ 正确
<div className="container">
JSX工作原理
JSX不是合法的JavaScript语法,它需要被转译。React使用Babel或TypeScript编译器将JSX转换为React.createElement()调用。
// JSX
const element = <h1 className="greeting">Hello, world!</h1>;
// 转译后
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
本章小结
- JSX是JavaScript的语法扩展
- 在JSX中可以使用{}嵌入表达式
- 使用map渲染列表时必须提供key属性
- 事件处理使用onClick等驼峰命名属性
- 样式可以使用内联对象或className
延伸阅读
下一章
---
#### 1.3 组件与Props (Components & Props)
**文档路径**: `docs/foundations/components-props.mdx`
**预估时长**: 60分钟
**难度**: 初级
**内容大纲**:
```markdown
# 组件与Props
## 本章学习目标
- 理解React组件的概念和作用
- 掌握函数组件和类组件的写法
- 学会使用Props传递数据
- 理解Props的只读性
- 掌握Props的类型验证
## 1. 什么是组件?
组件是React应用的基本构建块,它是可复用的UI片段。
### 组件特性
- 可组合:多个组件组合成复杂UI
- 可复用:一次编写,多处使用
- 可维护:逻辑和样式封装
- 可测试:独立测试
## 2. 函数组件 vs 类组件
### 2.1 函数组件 (推荐)
```jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
2.2 类组件 (传统写法)
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
对比演示
<CodePlayground files={{ '/components/FunctionalWelcome.js': ` import React from 'react';
function FunctionalWelcome(props) { return ( <div style={{ border: '2px solid #28a745', padding: '20px', margin: '10px' }}>
函数组件
Hello, {props.name}! You are {props.age} years old.
); }export default FunctionalWelcome;
, '/components/ClassWelcome.js':
import React from 'react';
class ClassWelcome extends React.Component { render() { return ( <div style={{ border: '2px solid #007bff', padding: '20px', margin: '10px' }}>
类组件
Hello, {this.props.name}! You are {this.props.age} years old.
); } }export default ClassWelcome;
, '/App.js':
import React from 'react';
import FunctionalWelcome from './components/FunctionalWelcome';
import ClassWelcome from './components/ClassWelcome';
function App() { return ( <div style={{ padding: '20px' }}>
组件类型对比
); }export default App; ` }} />
3. Props详解
3.1 Props是什么?
Props是组件的输入数据,它们从父组件传递给子组件。
3.2 传递Props
<LiveCode code={` function App() { return (
function UserCard(props) { // 解构赋值 const { name, age, occupation } = props;
return ( <div style={{ border: '1px solid #ddd', borderRadius: '8px', padding: '15px', margin: '10px 0' }}>
{name}
Age: {age}
Occupation: {occupation}
); } `} />3.3 Props的只读性
Props是只读的,不能在子组件中修改。
<LiveCode code={` function App() { return ; }
function Counter() { let count = 0; // 这不是state,组件重新渲染时会被重置
const increment = () => { count++; // ❌ 错误!不能直接修改props alert(count); };
return (
Count: {count}
Increment <p style={{ color: 'red', fontSize: '12px' }}> This won't work properly! Use useState instead.4. 组件组合
4.1 组合多个组件
<LiveCode code={` function App() { return (
function Header() { return <header style={{ background: '#333', color: 'white', padding: '20px' }}>
My Website
; }function MainContent() { return <main style={{ padding: '20px' }}>
Main Content
This is the main content area.
; }function Footer() { return <footer style={{ background: '#f0f0f0', padding: '20px', textAlign: 'center' }}>
© 2024 My Website
; } `} />4.2 容器组件 vs 展示组件
<LiveCode code={` // 容器组件 - 负责逻辑 function UserListContainer() { const users = [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' } ];
return ; }
// 展示组件 - 负责UI function UserList({ users }) { return (
-
{users.map(user => (
- {user.name} ))}
5. Props类型验证
5.1 PropTypes (运行时检查)
yarn add prop-types
import PropTypes from 'prop-types';
function UserCard({ name, age, isActive }) {
return (
<div>
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Status: {isActive ? 'Active' : 'Inactive'}</p>
</div>
);
}
UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
isActive: PropTypes.bool
};
5.2 TypeScript (编译时检查)
interface UserCardProps {
name: string;
age?: number;
isActive: boolean;
}
function UserCard({ name, age, isActive }: UserCardProps) {
return (
<div>
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Status: {isActive ? 'Active' : 'Inactive'}</p>
</div>
);
}
5.3 默认Props
function UserCard({ name, age = 18, isActive = false }) {
return (
<div>
<h3>{name}</h3>
<p>Age: {age}</p>
<p>Status: {isActive ? 'Active' : 'Inactive'}</p>
</div>
);
}
UserCard.defaultProps = {
age: 18,
isActive: false
};
6. 特殊Props
6.1 children
function Card({ children }) {
return (
<div style={{ border: '1px solid #ccc', padding: '20px' }}>
{children}
</div>
);
}
function App() {
return (
<Card>
<h2>Card Title</h2>
<p>This is card content</p>
</Card>
);
}
6.2 ref (forwardRef)
const Input = React.forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
const inputRef = useRef(null);
<Input ref={inputRef} />
6.3 className和style
function Button({ children, variant = 'primary', ...props }) {
const baseStyle = {
padding: '10px 20px',
border: 'none',
borderRadius: '5px',
cursor: 'pointer'
};
const variantStyle = {
primary: { background: '#007bff', color: 'white' },
secondary: { background: '#6c757d', color: 'white' }
};
return (
<button
style={{ ...baseStyle, ...variantStyle[variant] }}
{...props}
>
{children}
</button>
);
}
实践练习
练习1:创建Button组件
创建一个可复用的Button组件,支持:
- 不同变体(primary, secondary, danger)
- 不同尺寸(small, medium, large)
- 可选图标
- 加载状态
练习2:创建Form组件
创建一个Form组件,包含:
- 输入字段(姓名、邮箱、密码)
- 验证逻辑
- 提交处理
练习3:创建Widget组件
创建一个Dashboard Widget组件,包含:
- 标题
- 内容区域
- 操作按钮
- 可折叠/展开
组件设计最佳实践
1. 单一职责原则
每个组件只负责一个功能。
2. Props最小化
只传递组件真正需要的props。
3. 避免深层嵌套
深层嵌套会增加复杂性,考虑使用composition。
4. 保持组件纯函数
相同输入应该产生相同输出。
5. 使用解构赋值
// ✅ 好
function UserCard({ name, age, occupation }) {
return <div>{name}</div>;
}
// ❌ 不好
function UserCard(props) {
return <div>{props.name}</div>;
}
常见错误
1. 修改Props
// ❌ 错误
function MyComponent(props) {
props.title = 'New Title'; // 不能修改props
return <div>{props.title}</div>;
}
// ✅ 正确
function MyComponent(props) {
const newTitle = props.title + '!';
return <div>{newTitle}</div>;
}
2. 忘记key属性
// ❌ 错误
{items.map(item => <div>{item}</div>)}
// ✅ 正确
{items.map(item => <div key={item.id}>{item}</div>)}
3. 直接传递对象而不解构
// ❌ 可以,但不够清晰
<User user={user} />
// ✅ 更好
<User name={user.name} email={user.email} />
本章小结
- 组件是React的基本构建块
- 函数组件是现代React的首选写法
- Props是组件的输入,是只读的
- 使用组合来构建复杂UI
- PropTypes和TypeScript可以提高代码质量
延伸阅读
下一章
---
### 模块2: React Hooks 详解
#### 2.1 useState Hook (基础)
**文档路径**: `docs/hooks/basics/useState.mdx`
**预估时长**: 60分钟
**难度**: 初级
**内容大纲**:
```markdown
# useState Hook 深入解析
## 本章学习目标
- 理解useState Hook的工作原理
- 掌握useState的基本语法
- 学会管理不同类型的状态
- 理解状态更新的异步特性
- 掌握函数式更新
## 1. 什么是useState?
useState是React提供的Hook,用于在函数组件中添加状态。
### 为什么需要useState?
- 函数组件在React 16.8之前无法使用状态
- Hook让函数组件具有类组件的所有能力
- 更简洁、更易测试、更易复用逻辑
## 2. useState基础语法
### 2.1 基本语法
```jsx
const [state, setState] = useState(initialValue);
state: 当前状态值setState: 更新状态的函数initialValue: 状态的初始值
2.2 第一个例子:计数器
<LiveCode code={` function Counter() { const [count, setCount] = useState(0);
return ( <div style={{ padding: '20px', textAlign: 'center' }}>
计数器
<p style={{ fontSize: '24px', fontWeight: 'bold' }}>{count} <button onClick={() => setCount(count + 1)} style={{ margin: '5px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer' }} > + <button onClick={() => setCount(count - 1)} style={{ margin: '5px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer' }} > - <button onClick={() => setCount(0)} style={{ margin: '5px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer' }} > 重置 ); } `} />3. 管理不同类型的状态
3.1 数字类型
<LiveCode code={` function NumberInput() { const [number, setNumber] = useState(0);
return (
数字输入
<input type="number" value={number} onChange={e => setNumber(parseInt(e.target.value) || 0)} style={{ padding: '5px', fontSize: '16px' }} />当前值: {number}
平方: {number * number}
3.2 字符串类型
<LiveCode code={` function TextInput() { const [text, setText] = useState('');
return (
文本输入
<input type="text" value={text} onChange={e => setText(e.target.value)} placeholder="输入一些文字..." style={{ padding: '5px', fontSize: '16px', width: '300px' }} />输入的文字: {text}
字符数: {text.length}
3.3 布尔类型
<LiveCode code={` function Toggle() { const [isOn, setIsOn] = useState(false);
return (
开关控制
<button onClick={() => setIsOn(!isOn)} style={{ width: '60px', height: '30px', borderRadius: '15px', border: 'none', cursor: 'pointer', backgroundColor: isOn ? '#4CAF50' : '#ccc', color: 'white', transition: 'background-color 0.3s' }} > {isOn ? 'ON' : 'OFF'}状态: {isOn ? '开启' : '关闭'}
<div style={{ marginTop: '20px', padding: '20px', backgroundColor: isOn ? '#e8f5e9' : '#f5f5f5', transition: 'background-color 0.3s' }}>灯是{isOn ? '亮着' : '关着'}的
3.4 对象类型
<LiveCode code={` function UserProfile() { const [user, setUser] = useState({ name: '', email: '', age: '' });
const updateField = (field, value) => { setUser(prev => ({ ...prev, [field]: value })); };
return (
用户资料
<div style={{ marginBottom: '10px' }}> 姓名: <input type="text" value={user.name} onChange={e => updateField('name', e.target.value)} style={{ padding: '5px' }} />预览:
姓名: {user.name}
邮箱: {user.email}
年龄: {user.age}
); } `} />3.5 数组类型
<LiveCode code={` function TodoList() { const [items, setItems] = useState([]); const [inputValue, setInputValue] = useState('');
const addItem = () => { if (inputValue.trim()) { setItems(prev => [...prev, { id: Date.now(), text: inputValue, completed: false }]); setInputValue(''); } };
const toggleItem = (id) => { setItems(prev => prev.map(item => item.id === id ? { ...item, completed: !item.completed } : item )); };
const deleteItem = (id) => { setItems(prev => prev.filter(item => item.id !== id)); };
return (
待办事项列表
4. 函数式更新
4.1 为什么需要函数式更新?
当新状态依赖于前一个状态时,应该使用函数式更新。
<LiveCode code={` function CounterWithFunction() { const [count, setCount] = useState(0);
return (
计数器(函数式更新)
Count: {count}
<button onClick={() => setCount(c => c + 1)}>递增 <button onClick={() => setCount(c => c - 1)}>递减 <button onClick={() => setCount(0)}>重置4.2 函数式更新的必要性
<LiveCode code={` function BuggyCounter() { const [count, setCount] = useState(0);
const incrementThreeTimes = () => { setCount(count + 1); // 会覆盖前面的更新 setCount(count + 1); setCount(count + 1); };
return ( <div style={{ padding: '15px', border: '1px solid red', marginBottom: '20px' }}>
❌ 错误的做法
Count: {count}
点击三次(只会增加1) <p style={{ color: 'red', fontSize: '12px' }}> 问题:多次调用setCount会相互覆盖 ); }function CorrectCounter() { const [count, setCount] = useState(0);
const incrementThreeTimes = () => { setCount(c => c + 1); // 使用函数式更新 setCount(c => c + 1); setCount(c => c + 1); };
return ( <div style={{ padding: '15px', border: '1px solid green' }}>
✅ 正确的做法
Count: {count}
点击三次(会增加3) <p style={{ color: 'green', fontSize: '12px' }}> 正确:每次更新都基于前一次的状态 ); } `} />5. 状态初始值
5.1 静态初始值
const [count, setCount] = useState(0);
5.2 函数初始值(懒初始化)
当初始值需要复杂计算时,使用函数:
const [state, setState] = useState(() => {
// 只在组件初始化时执行一次
return expensiveComputation();
});
<LiveCode code={` function ExpensiveComponent() { const [users, setUsers] = useState(() => { console.log('执行昂贵计算...'); return [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' } ]; });
return (
用户列表(函数初始值)
用户数量: {users.length}
<button onClick={() => setUsers([])}> 清空列表 <button onClick={() => window.location.reload()}> 重新加载组件 <p style={{ fontSize: '12px', color: '#666' }}> 打开控制台查看"执行昂贵计算..."只会在组件初始化时输出一次6. useState的规则
6.1 只能在顶层调用
// ❌ 错误 - 在循环中调用
function MyComponent() {
for (let i = 0; i < 10; i++) {
const [state, setState] = useState(0); // 错误!
}
return <div>...</div>;
}
// ✅ 正确 - 在顶层调用
function MyComponent() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
const [count3, setCount3] = useState(0);
return <div>...</div>;
}
6.2 只能在函数组件或自定义Hook中调用
// ❌ 错误 - 在类组件中
class MyClassComponent extends React.Component {
state = { count: 0 };
// 不能在这里调用useState
}
// ✅ 正确 - 在函数组件中
function MyFunctionComponent() {
const [count, setCount] = useState(0);
return <div>Count: {count}</div>;
}
实践练习
练习1:简单的待办事项应用
创建一个Todo应用,支持:
- 添加任务
- 标记完成
- 删除任务
- 过滤任务(全部/已完成/未完成)
练习2:表单组件
创建一个登录表单,包含:
- 用户名输入
- 密码输入
- 显示/隐藏密码
- 表单验证
练习3:颜色选择器
创建一个颜色选择器,包含:
- 颜色预览
- RGB滑块
- 十六进制显示
- 预设颜色
常见错误
1. 直接修改状态
// ❌ 错误
function MyComponent() {
const [items, setItems] = useState([]);
const addItem = () => {
items.push(newItem); // 直接修改数组
setItems(items); // 这样不会触发重新渲染
};
return <div>...</div>;
}
// ✅ 正确
function MyComponent() {
const [items, setItems] = useState([]);
const addItem = () => {
setItems(prev => [...prev, newItem]); // 创建新数组
};
return <div>...</div>;
}
2. 忘记解构数组
// ❌ 可以工作但不清晰
const state = useState(0);
const setState = state[1];
// ✅ 清晰明了
const [state, setState] = useState(0);
3. 在循环、条件或嵌套函数中调用
// ❌ 错误
function MyComponent() {
if (condition) {
const [state, setState] = useState(0);
}
const handleClick = () => {
const [state, setState] = useState(0);
};
return <div>...</div>;
}
// ✅ 正确
function MyComponent() {
const [state, setState] = useState(0);
return <div>...</div>;
}
性能优化
1. 合理拆分状态
// ❌ 不好 - 无关的状态放在一起
const [form, setForm] = useState({
name: '',
email: '',
theme: 'light'
});
// ✅ 更好 - 相关状态放在一起,无关状态分开
const [user, setUser] = useState({ name: '', email: '' });
const [theme, setTheme] = useState('light');
2. 避免频繁状态更新
// ❌ 每次输入都更新状态(性能较差)
<input onChange={e => setText(e.target.value)} />
// ✅ 或者使用防抖
const [text, setText] = useState('');
const debouncedSetText = useMemo(
() => debounce(setText, 300),
[]
);
本章小结
- useState是React Hook,用于在函数组件中添加状态
- 使用数组解构获取状态值和更新函数
- 对于对象和数组状态,使用展开运算符创建新引用
- 当新状态依赖前一个状态时,使用函数式更新
- useState只能在函数组件的顶层调用
延伸阅读
下一章
---
## 📐 线框图与原型设计
### 1. 学习中心主页线框图
┌─────────────────────────────────────────────────────────────┐ │ React Learning Center [Search] [GitHub] │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 🎯 你的React学习之旅从这里开始 │ │ │ │ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ │ │ 🚀 基础入门 │ │ 🎣 React Hooks│ │ 🧩 组件模式 │ │ │ │ 8篇文档 │ │ 20篇文档 │ │ 12篇文档 │ │ │ │ [开始学习] │ │ [开始学习] │ │ [开始学习] │ │ │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ │ ┌───────────────┐ ┌───────────────┐ │ │ │ 🌊 状态管理 │ │ ⚡ 性能优化 │ │ │ │ 10篇文档 │ │ 8篇文档 │ │ │ │ [开始学习] │ │ [开始学习] │ │ │ └───────────────┘ └───────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ 你的学习进度 │ │ │ ⬜ 基础入门 (0/8) │ │ │ ⬜ React Hooks (0/20) │ │ │ ⬜ 组件模式 (0/12) │ │ │ ⬜ 状态管理 (0/10) │ │ │ ⬜ 性能优化 (0/8) │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ 推荐学习路径 │ │ │ │ │ │ [基础入门] → [React Hooks] → [组件模式] │ │ │ ↓ ↓ ↓ │ │ │ [状态管理] → [性能优化] → [实战项目] │ │ └─────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────┘
### 2. 文档页面线框图
┌─────────────────────────────────────────────────────────────┐ │ React Learning Center [Search] [GitHub] │ ├─────────────────────────────────────────────────────────────┤ │ ← 返回上一页 [Progress] 33% │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 📚 基础入门 / 什么是React [Progress] │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ 本章学习目标 │ │ │ • 理解React的核心概念 │ │ │ • 了解React的历史和设计哲学 │ │ │ • 掌握React的优势和适用场景 │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ ─────────────────────────────────────────────────────────── │ │ │ │ ## 1. 什么是React? │ │ │ │ React是由Facebook(现Meta)开发的前端框架... │ │ │ │ ### 交互式示例1:Hello React │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ [App.js] [index.js] │ ┌─────────────────────────┐ │ │ │ import React from 'react' │ │ Hello React! │ │ │ │ │ │ Welcome to your... │ │ │ │ function App() { │ └─────────────────────────┘ │ │ │ return
Hello...
│ │ │ │ } │ [Console] │ │ └─────────────────────────────────┘ [Refresh] [Theme] │ │ │ │ 📋 代码已复制到剪贴板 ✓ │ │ │ │ ─────────────────────────────────────────────────────────── │ │ │ │ [上一章:学习路线图] [下一章:JSX] │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ 💬 有什么问题?添加评论... [提交] │ │ └─────────────────────────────────────────────────────────────┘ │ │ └─────────────────────────────────────────────────────────────┘
### 3. 学习路径可视化组件
```jsx
function LearningPathDiagram() {
const path = [
{ id: 'foundations', title: '基础入门', completed: true, current: false },
{ id: 'hooks-basic', title: 'Hooks基础', completed: true, current: true },
{ id: 'hooks-advanced', title: 'Hooks进阶', completed: false, current: false },
{ id: 'patterns', title: '组件模式', completed: false, current: false },
{ id: 'state', title: '状态管理', completed: false, current: false },
{ id: 'performance', title: '性能优化', completed: false, current: false }
];
return (
<div style={{ padding: '40px', background: '#f8f9fa' }}>
<h2 style={{ textAlign: 'center', marginBottom: '40px' }}>
你的React学习路径
</h2>
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
flexWrap: 'wrap',
maxWidth: '1000px',
margin: '0 auto'
}}>
{path.map((node, index) => (
<React.Fragment key={node.id}>
<div style={{
position: 'relative',
width: '120px',
height: '120px',
borderRadius: '50%',
border: `3px solid ${
node.completed ? '#28a745' :
node.current ? '#007bff' : '#dee2e6'
}`,
background: node.completed ? '#28a745' :
node.current ? '#007bff' : '#fff',
color: node.completed || node.current ? '#fff' : '#6c757d',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
transition: 'transform 0.2s',
boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
}}>
<div style={{ fontSize: '14px', fontWeight: 'bold' }}>
{node.title.split(' ')[0]}
</div>
<div style={{ fontSize: '11px', marginTop: '4px' }}>
{node.title.split(' ').slice(1).join(' ')}
</div>
{node.completed && (
<div style={{
position: 'absolute',
top: '-8px',
right: '-8px',
background: '#28a745',
borderRadius: '50%',
width: '24px',
height: '24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontSize: '14px'
}}>
✓
</div>
)}
</div>
{index < path.length - 1 && (
<div style={{
width: '80px',
height: '2px',
background: path[index + 1].completed || path[index + 1].current
? '#007bff'
: '#dee2e6',
margin: '0 20px'
}} />
)}
</React.Fragment>
))}
</div>
<div style={{
marginTop: '40px',
textAlign: 'center',
padding: '20px',
background: 'white',
borderRadius: '8px',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
}}>
<h3>当前进度</h3>
<div style={{
width: '400px',
height: '20px',
background: '#e9ecef',
borderRadius: '10px',
margin: '20px auto',
overflow: 'hidden'
}}>
<div style={{
width: '33%',
height: '100%',
background: 'linear-gradient(to right, #007bff, #0056b3)',
transition: 'width 0.3s'
}} />
</div>
<p style={{ color: '#6c757d' }}>
已完成 2/6 个模块 · 完成度 33%
</p>
<button style={{
padding: '12px 24px',
background: '#007bff',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '16px',
marginTop: '10px'
}}>
继续学习 Hooks进阶
</button>
</div>
</div>
);
}
🎨 交互式组件设计规范
CodePlayground 样式规范
/* CodePlayground 容器 */
.sandpack-wrapper {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
margin: 20px 0;
}
/* 主题切换按钮 */
.theme-toggle {
position: absolute;
top: 10px;
right: 10px;
z-index: 10;
padding: 6px 12px;
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: background 0.2s;
}
.theme-toggle:hover {
background: rgba(0, 0, 0, 0.7);
}
/* 控制台输出 */
.sandpack-console {
background: #1e1e1e;
color: #d4d4d4;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
}
/* 代码复制按钮 */
.copy-button {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 8px;
background: rgba(0, 0, 0, 0.6);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
opacity: 0;
transition: opacity 0.2s;
}
.code-block:hover .copy-button {
opacity: 1;
}
.copy-button.copied {
background: #28a745;
}
LiveCode 样式规范
/* LiveCode 容器 */
.live-wrapper {
border: 2px solid #e9ecef;
border-radius: 8px;
overflow: hidden;
margin: 20px 0;
}
.live-editor {
background: #f8f9fa;
font-size: 14px;
}
.live-preview {
padding: 20px;
background: white;
min-height: 100px;
}
.live-error {
background: #f8d7da;
color: #721c24;
padding: 10px;
font-family: monospace;
font-size: 12px;
}
/* 加载状态 */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #007bff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
📝 内容创作模板
文档页面前置代码
---
id: {document-id}
title: {文档标题}
description: {文档描述}
sidebar_position: {number}
tags: [{tag1}, {tag2}, {tag3}]
---
# {文档标题}
import { CodePlayground, LiveCode, CopyButton } from '@components';
## 本章学习目标
- {学习目标1}
- {学习目标2}
- {学习目标3}
## 概述
{概述内容}
## 示例
<CodePlayground
files={{
{文件路径}: `
{代码内容}
`
}}
options={{}}
/>
## 练习
### 练习{编号}:{练习标题}
{练习描述}
<Exercise
instructions={...}
starterCode={...}
solution={...}
/>
## 常见错误
### {错误标题}
```jsx
// ❌ 错误代码
{错误代码示例}
// ✅ 正确代码
{正确代码示例}
本章小结
- {要点1}
- {要点2}
- {要点3}
延伸阅读
下一章
---
## 📊 数据结构设计
### 学习进度追踪
```typescript
interface LearningProgress {
userId: string;
completedTopics: string[];
currentTopic: string;
completionPercentage: number;
lastAccessed: Date;
timeSpent: number; // 秒
bookmarkedTopics: string[];
quizScores: Record<string, number>;
}
文档元数据
interface DocumentMetadata {
id: string;
title: string;
description: string;
path: string;
tags: string[];
difficulty: 'beginner' | 'intermediate' | 'advanced';
estimatedTime: number; // 分钟
prerequisites?: string[];
learningObjectives: string[];
codeExamples: CodeExample[];
exercises: Exercise[];
}
交互式示例
interface CodeExample {
id: string;
title: string;
description: string;
files: Record<string, string>;
entry: string;
options: SandpackOptions;
explanations?: string[];
}
这份详细设计文档提供了完整的实施蓝图,包括组件API、学习路径详细内容、线框图和设计规范。可以作为开发的直接参考文档。