Files
mintlify[bot] 5a3d1ddabb docs: sync es/fr/zh translations with recent English updates (#6140)
* docs: sync es/fr/zh translations with recent English updates

* docs: tighten descriptions and replace HTML entities with real quotes

---------

Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-06-10 11:49:25 -07:00

266 lines
8.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "React"
description: "使用 JSX、状态和客户端逻辑,在你的 Mintlify 文档中通过自定义 React 组件构建交互且可复用的元素。"
keywords: ["React 组件", "交互式组件", "JSX", "自定义组件"]
boost: 3
---
import { ColorGenerator } from "/snippets/color-generator.jsx";
直接在 MDX 文件中使用 [React 组件](https://react.dev) 和 [hooks](https://react.dev/reference/react/hooks) 在文档中构建交互式元素。
<div id="inline-components">
## 内联组件
</div>
直接在你的 MDX 文件中声明组件:
export const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return (
<div className="flex items-center justify-center">
<div className="flex items-center rounded-xl overflow-hidden border border-zinc-950/20 dark:border-white/20">
<button
onClick={decrement}
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-r border-zinc-950/20 dark:border-white/20"
aria-label="Decrease"
>
-
</button>
<div className="flex text-sm items-center justify-center h-8 px-6 text-zinc-950/80 dark:text-white/80 font-medium min-w-[4rem] text-center">
{count}
</div>
<button
onClick={increment}
className="flex items-center justify-center h-8 w-8 text-zinc-950/80 dark:text-white/80 border-l border-zinc-950/20 dark:border-white/20"
aria-label="Increase"
>
+
</button>
</div>
</div>
)
}
<Counter />
```mdx
export const Counter = () => {
const [count, setCount] = useState(0)
const increment = () => setCount(count + 1)
const decrement = () => setCount(count - 1)
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
)
}
<Counter />
```
<div id="import-components">
## 导入组件
</div>
组件文件必须位于 `/snippets/` 文件夹中。更多信息请参阅 [可复用片段](/zh/create/reusable-snippets)。
<Note>
不支持嵌套导入。请将所有被引用的组件直接导入到父 MDX 文件中。
</Note>
在 `snippets/` 中创建一个组件文件:
```mdx /snippets/color-generator.jsx [expandable]
export const ColorGenerator = () => {
const [hue, setHue] = useState(180)
const [saturation, setSaturation] = useState(50)
const [lightness, setLightness] = useState(50)
const [colors, setColors] = useState([])
useEffect(() => {
const newColors = []
for (let i = 0; i < 5; i++) {
const l = Math.max(10, Math.min(90, lightness - 20 + i * 10))
newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`)
}
setColors(newColors)
}, [hue, saturation, lightness])
const copyToClipboard = (color) => {
navigator.clipboard
.writeText(color)
.then(() => {
console.log(`Copied ${color} to clipboard!`)
})
.catch((err) => {
console.error("Failed to copy: ", err)
})
}
return (
<div className="p-4 border dark:border-zinc-950/80 rounded-xl not-prose">
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Hue: {hue}°
<input
type="range"
min="0"
max="360"
value={hue}
onChange={(e) => setHue(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(0, ${saturation}%, ${lightness}%),
hsl(60, ${saturation}%, ${lightness}%),
hsl(120, ${saturation}%, ${lightness}%),
hsl(180, ${saturation}%, ${lightness}%),
hsl(240, ${saturation}%, ${lightness}%),
hsl(300, ${saturation}%, ${lightness}%),
hsl(360, ${saturation}%, ${lightness}%))`,
}}
/>
</label>
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Saturation: {saturation}%
<input
type="range"
min="0"
max="100"
value={saturation}
onChange={(e) => setSaturation(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, 0%, ${lightness}%),
hsl(${hue}, 50%, ${lightness}%),
hsl(${hue}, 100%, ${lightness}%))`,
}}
/>
</label>
<label className="block text-sm text-zinc-950/70 dark:text-white/70">
Lightness: {lightness}%
<input
type="range"
min="0"
max="100"
value={lightness}
onChange={(e) => setLightness(Number.parseInt(e.target.value))}
className="w-full h-2 bg-zinc-950/20 rounded-lg appearance-none cursor-pointer dark:bg-white/20 mt-1"
style={{
background: `linear-gradient(to right,
hsl(${hue}, ${saturation}%, 0%),
hsl(${hue}, ${saturation}%, 50%),
hsl(${hue}, ${saturation}%, 100%))`,
}}
/>
</label>
</div>
<div className="flex space-x-1">
{colors.map((color, idx) => (
<div
key={idx}
className="h-16 rounded flex-1 cursor-pointer transition-transform hover:scale-105"
style={{ backgroundColor: color }}
title={`Click to copy: ${color}`}
onClick={() => copyToClipboard(color)}
/>
))}
</div>
<div className="text-sm font-mono text-zinc-950/70 dark:text-white/70">
<p>
Base color: hsl({hue}, {saturation}%, {lightness}%)
</p>
</div>
</div>
</div>
)
}
```
然后导入并使用它:
```mdx
import { ColorGenerator } from "/snippets/color-generator.jsx"
<ColorGenerator />
```
<ColorGenerator />
<div id="considerations">
## 注意事项
</div>
- **SEO**:搜索引擎可能无法完全索引由客户端渲染的动态内容。
- **首次加载**:访客在组件渲染之前可能会看到闪烁。
- **可访问性**:确保屏幕阅读器能够播报动态内容的变化。
- **优化依赖数组**:仅在 `useEffect` 中包含必要的依赖。
- **缓存昂贵操作**:在适当之处使用 `useMemo` 或 `useCallback`。
- **减少重新渲染**:将大型组件拆分为较小的组件。
- **懒加载**:延迟渲染复杂组件,以缩短首次页面加载时间。由于 MDX 沙箱不支持 `React.lazy` 和动态 `import()`,请改为通过用户交互或可见性来控制重量级组件的加载。参见[延迟渲染重量级组件](#defer-rendering-for-heavy-components)。
<div id="defer-rendering-for-heavy-components">
### 延迟渲染重量级组件
</div>
MDX 沙箱中无法使用 `React.lazy`、`Suspense` 和动态 `import()`。为获得同样的效果,先渲染一个轻量级的占位符,仅在读者主动选择后再挂载开销较大的组件。这样可以保持首次页面加载的速度,同时仍然让读者能够与完整的组件交互。
下面的示例会让上一节中的 `ColorGenerator` 保持未挂载状态,直到读者点击 **Load color generator**:
export const LazyColorGenerator = () => {
const [show, setShow] = useState(false)
if (!show) {
return (
<button
onClick={() => setShow(true)}
className="px-3 py-1.5 text-sm rounded-lg border border-zinc-950/20 dark:border-white/20 text-zinc-950/80 dark:text-white/80"
>
Load color generator
</button>
)
}
return <ColorGenerator />
}
<LazyColorGenerator />
```mdx
import { ColorGenerator } from "/snippets/color-generator.jsx"
export const LazyColorGenerator = () => {
const [show, setShow] = useState(false)
if (!show) {
return (
<button onClick={() => setShow(true)}>
Load color generator
</button>
)
}
return <ColorGenerator />
}
<LazyColorGenerator />
```
如需在组件滚动到可见区域后再进行渲染,可将按钮替换为在 `useEffect` 中设置的 `IntersectionObserver`。模式相同:在触发器触发之前保持 `show` 为 `false`,然后返回重量级组件。