docs-sandpack
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSandpack Patterns
Sandpack 使用模式
Quick Start Template
快速开始模板
Most examples are single-file. Copy this and modify:
mdx
<Sandpack>
` ` `js
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState(0);
return (
<button onClick={() => setValue(value + 1)}>
Clicked {value} times
</button>
);
}
` ` `
</Sandpack>大多数示例为单文件形式。可复制以下模板进行修改:
mdx
<Sandpack>
` ` `js
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState(0);
return (
<button onClick={() => setValue(value + 1)}>
Clicked {value} times
</button>
);
}
` ` `
</Sandpack>File Naming
文件命名规则
| Pattern | Usage |
|---|---|
| Main file (no prefix) |
| Supporting files |
| Active file (reference pages) |
| Hidden files |
| CSS styles |
| External dependencies |
Critical: Main file must have .
export default| 写法模式 | 用途 |
|---|---|
| 主文件(无前缀) |
| 辅助文件 |
| 激活文件(参考页面) |
| 隐藏文件 |
| CSS样式文件 |
| 外部依赖配置 |
重点注意: 主文件必须包含。
export defaultLine Highlighting
代码行高亮
mdx
```js {2-4}
function Example() {
// Lines 2-4
// will be
// highlighted
return null;
}mdx
```js {2-4}
function Example() {
// 第2-4行
// 将被
// 高亮显示
return null;
}Code References (numbered callouts)
代码引用(编号标注)
mdx
```js [[1, 4, "age"], [2, 4, "setAge"]]
// Creates numbered markers pointing to "age" and "setAge" on line 4mdx
```js [[1, 4, "age"], [2, 4, "setAge"]]
// 在第4行创建指向"age"和"setAge"的编号标记Expected Errors (intentionally broken examples)
预期错误(故意编写的错误示例)
mdx
```js {expectedErrors: {'react-compiler': [7]}}
// Line 7 shows as expected errormdx
```js {expectedErrors: {'react-compiler': [7]}}
// 第7行会显示为预期错误Multi-File Example
多文件示例
mdx
<Sandpack>
```js src/App.js
import Gallery from './Gallery.js';
export default function App() {
return <Gallery />;
}js
export default function Gallery() {
return <h1>Gallery</h1>;
}css
h1 { color: purple; }mdx
<Sandpack>
```js src/App.js
import Gallery from './Gallery.js';
export default function App() {
return <Gallery />;
}js
export default function Gallery() {
return <h1>Gallery</h1>;
}css
h1 { color: purple; }External Dependencies
外部依赖配置
mdx
<Sandpack>
```js
import { useImmer } from 'use-immer';
// ...json
{
"dependencies": {
"immer": "1.7.3",
"use-immer": "0.5.1",
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest"
}
}mdx
<Sandpack>
```js
import { useImmer } from 'use-immer';
// ...json
{
"dependencies": {
"immer": "1.7.3",
"use-immer": "0.5.1",
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest"
}
}Code Style in Sandpack (Required)
Sandpack 代码样式要求(强制遵循)
Sandpack examples are held to strict code style standards:
- Function declarations for components (not arrows)
- for event parameters
e - Single quotes in JSX
- unless reassignment needed
const - Spaces in destructuring: not
({ props })({props}) - Two-line createRoot: separate declaration and render call
- Multiline if statements: always use braces
Sandpack示例需遵循严格的代码样式标准:
- 组件使用函数声明(而非箭头函数)
- 事件参数使用****表示
e - JSX中使用单引号
- 除非需要重新赋值,否则使用****
const - 解构赋值中添加空格:而非
({ props })({props}) - 分两行编写createRoot:将声明和渲染调用分开
- 多行if语句:始终使用大括号
Don't Create Hydration Mismatches
避免出现 Hydration 不匹配问题
Sandpack examples must produce the same output on server and client:
js
// 🚫 This will cause hydration warnings
export default function App() {
const isClient = typeof window !== 'undefined';
return <div>{isClient ? 'Client' : 'Server'}</div>;
}Sandpack示例必须在服务端和客户端生成相同的输出:
js
// 🚫 这会导致Hydration警告
export default function App() {
const isClient = typeof window !== 'undefined';
return <div>{isClient ? 'Client' : 'Server'}</div>;
}Use Ref for Non-Rendered State
针对非渲染状态使用Ref
js
// 🚫 Don't trigger re-renders for non-visual state
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
// ✅ Use ref instead
const mounted = useRef(false);
useEffect(() => { mounted.current = true; }, []);js
// 🚫 不要为非视觉状态触发重渲染
const [mounted, setMounted] = useState(false);
useEffect(() => { setMounted(true); }, []);
// ✅ 改用Ref
const mounted = useRef(false);
useEffect(() => { mounted.current = true; }, []);forwardRef and memo Patterns
forwardRef 和 memo 使用模式
forwardRef - Use Named Function
forwardRef - 使用命名函数
js
// ✅ Named function for DevTools display name
const MyInput = forwardRef(function MyInput(props, ref) {
return <input {...props} ref={ref} />;
});
// 🚫 Anonymous loses name
const MyInput = forwardRef((props, ref) => { ... });js
// ✅ 命名函数可在DevTools中显示组件名称
const MyInput = forwardRef(function MyInput(props, ref) {
return <input {...props} ref={ref} />;
});
// 🚫 匿名函数会丢失组件名称
const MyInput = forwardRef((props, ref) => { ... });memo - Use Named Function
memo - 使用命名函数
js
// ✅ Preserves component name
const Greeting = memo(function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
});js
// ✅ 保留组件名称
const Greeting = memo(function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
});Line Length
行长度规范
- Prose: ~80 characters
- Code: ~60-70 characters
- Break long lines to avoid horizontal scrolling
- 说明文字:约80个字符
- 代码:约60-70个字符
- 长行需换行,避免横向滚动
Anti-Patterns
反模式示例
| Pattern | Problem | Fix |
|---|---|---|
| Not standard | |
| Conflicts with global | |
| Re-renders | Use |
Reading | Hydration mismatch | Check in useEffect |
| Single-line if without braces | Harder to debug | Use multiline with braces |
Chained | Less clear | Two statements |
| Inconsistent | |
| Tabs | Inconsistent | 2 spaces |
| Deprecated | Use |
| Fake package names | Confusing | Use |
| Outdated | |
Missing | Warnings | Always include key |
| 模式 | 问题 | 修复方案 |
|---|---|---|
| 不符合标准 | |
| 与全局变量冲突 | |
为非渲染值使用 | 触发不必要重渲染 | 使用 |
渲染时读取 | Hydration不匹配 | 在useEffect中检查 |
| 无大括号的单行if语句 | 调试难度高 | 使用带大括号的多行写法 |
链式调用 | 可读性差 | 拆分为两个语句 |
| 格式不一致 | |
| 使用制表符 | 格式不一致 | 使用2个空格 |
| 已废弃 | 使用 |
| 虚假包名 | 易混淆 | 使用 |
| 已过时 | |
列表中缺少 | 触发警告 | 始终添加key |
Additional Code Quality Rules
额外代码质量规则
Always Include Keys in Lists
列表中始终包含Key
js
// ✅ Correct
{items.map(item => <li key={item.id}>{item.name}</li>)}
// 🚫 Wrong - missing key
{items.map(item => <li>{item.name}</li>)}js
// ✅ 正确写法
{items.map(item => <li key={item.id}>{item.name}</li>)}
// 🚫 错误写法 - 缺少key
{items.map(item => <li>{item.name}</li>)}Use Realistic Import Paths
使用真实感的导入路径
js
// ✅ Correct - descriptive path
import { fetchData } from './your-data-layer';
// 🚫 Wrong - looks like a real npm package
import { fetchData } from 'cool-data-lib';js
// ✅ 正确写法 - 路径具有描述性
import { fetchData } from './your-data-layer';
// 🚫 错误写法 - 看起来像真实的npm包
import { fetchData } from 'cool-data-lib';Console.log Labels
Console.log 添加标签
js
// ✅ Correct - labeled for clarity
console.log('User:', user);
console.log('Component Stack:', errorInfo.componentStack);
// 🚫 Wrong - unlabeled
console.log(user);js
// ✅ 正确写法 - 添加标签提升可读性
console.log('User:', user);
console.log('Component Stack:', errorInfo.componentStack);
// 🚫 错误写法 - 无标签
console.log(user);Keep Delays Reasonable
保持合理的延迟时间
js
// ✅ Correct - 1-1.5 seconds
setTimeout(() => setLoading(false), 1000);
// 🚫 Wrong - too long, feels sluggish
setTimeout(() => setLoading(false), 3000);js
// ✅ 正确写法 - 1-1.5秒
setTimeout(() => setLoading(false), 1000);
// 🚫 错误写法 - 时间过长,体验卡顿
setTimeout(() => setLoading(false), 3000);Updating Line Highlights
更新行高亮标记
When modifying code in examples with line highlights (), always update the highlight line numbers to match the new code. Incorrect line numbers cause rendering crashes.
{2-4}当修改带有行高亮标记()的示例代码时,务必更新高亮行号以匹配新代码。错误的行号会导致渲染崩溃。
{2-4}File Name Conventions
文件命名约定
- Capitalize file names for component files: not
Gallery.jsgallery.js - After initially explaining files are in , refer to files by name only:
src/notGallery.jssrc/Gallery.js
- 组件文件首字母大写:而非
Gallery.jsgallery.js - 在首次说明文件位于目录后,后续仅使用文件名指代:
src/而非Gallery.jssrc/Gallery.js
Naming Conventions in Code
代码中的命名约定
Components: PascalCase
- ,
Profile,Avatar,TodoListPackingList
State variables: Destructured pattern
const [count, setCount] = useState(0)- Booleans: ,
[isOnline, setIsOnline][isPacked, setIsPacked] - Status strings: ,
'typing','submitting','success''error'
Event handlers:
- ,
handleClick,handleSubmithandleAddTask
Props for callbacks:
- ,
onClick,onChange,onAddTaskonSelect
Custom Hooks:
- ,
useOnlineStatus,useChatRoomuseFormInput
Reducer actions:
- Past tense: ,
'added','changed''deleted' - Snake_case compounds: ,
'changed_selection''sent_message'
Updater functions: Single letter
setCount(n => n + 1)
组件: 大驼峰式(PascalCase)
- ,
Profile,Avatar,TodoListPackingList
状态变量: 解构模式
const [count, setCount] = useState(0)- 布尔值:,
[isOnline, setIsOnline][isPacked, setIsPacked] - 状态字符串:,
'typing','submitting','success''error'
事件处理函数:
- ,
handleClick,handleSubmithandleAddTask
回调函数Props:
- ,
onClick,onChange,onAddTaskonSelect
自定义Hooks:
- ,
useOnlineStatus,useChatRoomuseFormInput
Reducer动作:
- 过去式:,
'added','changed''deleted' - 蛇形命名复合词:,
'changed_selection''sent_message'
更新器函数: 单字母参数
setCount(n => n + 1)
Pedagogical Code Markers
教学用代码标记
Wrong vs right code:
js
// 🔴 Avoid: redundant state and unnecessary Effect
// ✅ Good: calculated during renderingConsole.log for lifecycle teaching:
js
console.log('✅ Connecting...');
console.log('❌ Disconnected.');错误与正确代码对比:
js
// 🔴 避免:冗余状态和不必要的Effect
// ✅ 推荐:在渲染时计算用于生命周期教学的Console.log:
js
console.log('✅ 连接中...');
console.log('❌ 已断开连接。');Server/Client Labeling
服务端/客户端组件标记
js
// Server Component
async function Notes() {
const notes = await db.notes.getAll();
}
// Client Component
"use client"
export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
}js
// 服务端组件
async function Notes() {
const notes = await db.notes.getAll();
}
// 客户端组件
"use client"
export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
}Bundle Size Annotations
包体积注释
js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)js
import marked from 'marked'; // 35.9K(压缩后11.2K)
import sanitizeHtml from 'sanitize-html'; // 206K(压缩后63.3K)Sandpack Example Guidelines
Sandpack 示例指南
Package.json Rules
Package.json 规则
Include package.json when:
- Using external npm packages (immer, remarkable, leaflet, toastify-js, etc.)
- Demonstrating experimental/canary React features
- Requiring specific React versions (,
react: beta)react: 19.0.0-rc-*
Omit package.json when:
- Example uses only built-in React features
- No external dependencies needed
- Teaching basic hooks, state, or components
Always mark package.json as hidden:
mdx
```json package.json hidden
{
"dependencies": {
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest",
"immer": "1.7.3"
}
}
**Version conventions:**
- Use `"latest"` for stable features
- Use exact versions only when compatibility requires it
- Include minimal dependencies (just what the example needs)需要包含package.json的场景:
- 使用外部npm包(immer、remarkable、leaflet、toastify-js等)
- 演示React实验性/预览版特性
- 需要指定特定React版本(,
react: beta)react: 19.0.0-rc-*
可以省略package.json的场景:
- 示例仅使用React内置特性
- 无需外部依赖
- 教学基础Hooks、状态管理或组件
始终将package.json标记为隐藏:
mdx
```json package.json hidden
{
"dependencies": {
"react": "latest",
"react-dom": "latest",
"react-scripts": "latest",
"immer": "1.7.3"
}
}
**版本约定:**
- 稳定特性使用`"latest"`
- 仅在兼容性要求时使用精确版本
- 仅包含示例所需的最小依赖Hidden File Patterns
隐藏文件规则
Always hide these file types:
| File Type | Reason |
|---|---|
| Configuration not the teaching point |
| Sandbox setup is boilerplate |
| HTML structure not the focus |
| When it contains sample/mock data |
| When showing API usage, not implementation |
| When styling is not the lesson |
| Supporting infrastructure |
| Server action implementation details |
Rationale:
- Reduces cognitive load
- Keeps focus on the primary concept
- Creates cleaner, more focused examples
Example:
mdx
```js src/data.js hidden
export const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
];undefined始终隐藏以下类型的文件:
| 文件类型 | 原因 |
|---|---|
| 配置信息非教学重点 |
| Sandbox配置为样板代码 |
| HTML结构非关注重点 |
| 包含示例/模拟数据时 |
| 展示API用法而非实现时 |
| 样式非教学内容时 |
| 辅助基础设施代码 |
| 服务端动作实现细节 |
设计思路:
- 降低认知负担
- 保持焦点在核心概念上
- 创建更简洁、聚焦的示例
示例:
mdx
```js src/data.js hidden
export const items = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' },
];undefinedActive File Patterns
激活文件规则
Mark as active when:
- File contains the primary teaching concept
- Learner should focus on this code first
- Component demonstrates the hook/pattern being taught
Effect of the marker:
active- Sets initial editor tab focus when Sandpack loads
- Signals "this is what you should study"
- Works with hidden files to create focused examples
Most common active file: or
src/index.jssrc/App.jsExample:
mdx
```js src/App.js active
// This file will be focused when example loads
export default function App() {
// ...
}undefined标记为active的场景:
- 文件包含核心教学概念
- 学习者应首先关注此代码
- 组件演示了所教授的Hook/模式
active- Sandpack加载时默认聚焦该编辑器标签
- 提示“这是你需要重点学习的内容”
- 与隐藏文件配合实现聚焦式示例
最常用的激活文件: 或
src/index.jssrc/App.js示例:
mdx
```js src/App.js active
// 示例加载时将聚焦此文件
export default function App() {
// ...
}undefinedFile Structure Guidelines
文件结构指南
| Scenario | Structure | Reason |
|---|---|---|
| Basic hook usage | Single file | Simple, focused |
| Teaching imports | 2-3 files | Shows modularity |
| Context patterns | 4-5 files | Realistic structure |
| Complex state | 3+ files | Separation of concerns |
Single File Examples (70% of cases):
- Use for simple concepts
- 50-200 lines typical
- Best for: Counter, text inputs, basic hooks
Multi-File Examples (30% of cases):
- Use when teaching modularity/imports
- Use for context patterns (4-5 files)
- Use when component is reused
File Naming:
- Main component: (capitalized)
App.js - Component files: ,
Gallery.js(capitalized)Button.js - Data files: (lowercase)
data.js - Utility files: (lowercase)
utils.js - Context files: (named after what they provide)
TasksContext.js
| 场景 | 结构 | 原因 |
|---|---|---|
| 基础Hook用法 | 单文件 | 简洁、聚焦 |
| 教学导入语法 | 2-3个文件 | 展示模块化特性 |
| Context模式 | 4-5个文件 | 贴近真实项目结构 |
| 复杂状态管理 | 3个以上文件 | 关注点分离 |
单文件示例(70%的场景):
- 用于简单概念教学
- 通常50-200行代码
- 最适合:计数器、文本输入框、基础Hook
多文件示例(30%的场景):
- 用于教授模块化/导入语法时
- 用于Context模式教学(4-5个文件)
- 组件需要复用的场景
文件命名:
- 主组件:(首字母大写)
App.js - 组件文件:,
Gallery.js(首字母大写)Button.js - 数据文件:(小写)
data.js - 工具文件:(小写)
utils.js - Context文件:(根据提供的内容命名)
TasksContext.js
Code Size Limits
代码长度限制
- Single file: <200 lines
- Multi-file total: 150-300 lines
- Main component: 100-150 lines
- Supporting files: 20-40 lines each
- 单文件:<200行
- 多文件总长度:150-300行
- 主组件:100-150行
- 辅助文件:20-40行/个
CSS Guidelines
CSS 指南
Always:
- Include minimal CSS for demo interactivity
- Use semantic class names (,
.panel,.button-primary).panel-dark - Support light/dark themes when showing UI concepts
- Keep CSS visible (never hidden)
Size Guidelines:
- Minimal (5-10 lines): Basic button styling, spacing
- Medium (15-30 lines): Panel styling, form layouts
- Complex (40+ lines): Only for layout-focused examples
必须遵循:
- 仅包含示例交互所需的最小CSS
- 使用语义化类名(,
.panel,.button-primary).panel-dark - 展示UI概念时支持明暗主题
- 保持CSS可见(绝不隐藏)
长度指南:
- 极简(5-10行):基础按钮样式、间距
- 中等(15-30行):面板样式、表单布局
- 复杂(40+行):仅用于布局相关示例