关于 react-design-patterns
面向 React 开发者的设计模式参考 skill,介绍 13 种组件与应用模式,并提供选择指南。建议先从 Custom Hook 入手,再按结构需求采用其他模式。
主要功能
- 01提供模式选择指南
- 02涵盖 13 种 React 设计模式
- 03说明受控与非受控组件
- 04介绍逻辑与呈现分离
- 05涵盖错误边界与 Portal
适用场景
- 01选择可复用组件逻辑的模式
- 02设计复合式 Tabs 或 Modal 组件
- 03分离数据获取与界面呈现
- 04规划设计系统结构
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ b4r7x/ agent-skills --skill react-design-patterns安装后在 Claude Code 中输入/react-design-patterns确认已加载。
详细介绍
React 设计模式
概述
按 2025 年的流行度排序的 13 种模式。黄金法则:先从自定义 Hook 开始——只有在需要结构共享时,才升级为复合组件。
模式选择指南
| 需求 | 模式 |
|---|---|
| 可复用逻辑,不限定 UI | 自定义 Hook |
| 受控与非受控组件行为 | 控制属性 |
| 紧密耦合的 UI 子组件(选项卡、模态框、手风琴) | 复合组件 |
| 不强加样式的逻辑 | 无头组件 |
| 将数据获取与渲染分离 | 容器 / 展示组件 |
| 全局稳定状态(身份验证、主题) | 提供者(Context) |
| 反向数据流(子组件 → 父组件) | 渲染属性 |
| 通过子元素完全控制渲染 | 函数子元素 |
| 带预构建属性的灵活 API | 属性 Getter |
| 崩溃隔离 | 错误边界 |
| 在 DOM 父元素之外渲染(模态框、工具提示) | Portal |
| 设计系统层级 | 原子设计 |
1. 自定义 Hook ⭐⭐⭐⭐⭐
将有状态逻辑提取到可复用函数中。2025 年最流行的模式。
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try { return JSON.parse(localStorage.getItem(key)) ?? initialValue; }
catch { return initialValue; }
});
const set = useCallback((v) => {
setValue(prev => {
const next = v instanceof Function ? v(prev) : v;
localStorage.setItem(key, JSON.stringify(next));
return next;
});
}, [key]);
return [value, set];
}
2. 控制属性 ⭐⭐⭐⭐
组件同时支持受控(父组件通过属性持有状态)和非受控(自行管理状态)模式。所有表单输入和组件库都使用此模式。
// Controlled — parent manages state
<EditableTitle title={title} onChange={setTitle} />
// Uncontrolled — component manages own state, parent can reset via key
<EditableTitle key={userId} defaultTitle="Untitled" />
// Implementation supporting both modes
function EditableTitle({ title, defaultTitle = '', onChange }) {
const [internal, setInternal] = useState(defaultTitle);
const isControlled = title !== undefined;
const value = isControlled ? title : internal;
const handleChange = (e) => {
if (!isControlled) setInternal(e.target.value);
onChange?.(e.target.value);
};
return <input value={value} onChange={handleChange} />;
}
3. 复合组件 ⭐⭐⭐⭐
通过本地 Context 共享状态的一组子组件。Radix UI、Headless UI 和 React Aria 都使用此模式。
const TabsContext = createContext(null);
function Tabs({ children, defaultTab }) { /* owns [activeTab, setActiveTab], provides both via context */ }
Tabs.Tab = /* reads context, renders the trigger button */
Tabs.Panel = /* reads context, renders children when active */
结构:父组件持有状态并通过本地 Context 提供状态;以点号命名空间组织的子组件使用该状态;useTabs() 防护 Hook 会在子组件于 <Tabs> 外部渲染时抛出错误。完整的可运行实现位于 react-usecontext 技能中(此示例的唯一来源)——构建复合组件时请加载该技能。
4. 无头组件 ⭐⭐⭐⭐
Hook 只提供逻辑,不包含任何 HTML 或 CSS。UI 完全由你掌控。
function useAccordion() {
const [openIndex, setOpenIndex] = useState(null);
const toggle = useCallback((i) => setOpenIndex(prev => prev === i ? null : i), []);
const isOpen = useCallback((i) => openIndex === i, [openIndex]);
return { toggle, isOpen };
}
// Two completely different UIs can use the same hook
5. 容器 / 展示组件 ⭐⭐⭐
拆分为:容器(逻辑、数据获取、导航)和展示组件(通过属性接收数据,只负责渲染)。
// Presentational — pure UI, easy to test
const UserCard = ({ user, onFollow }) => (
<div><h2>{user.name}</h2><button onClick={onFollow}>Follow</button></div>
);
// Container — logic and data
const UserCardContainer = ({ userId }) => {
const { user, follow } = useUser(userId);
if (!user) return <Skeleton />;
return <UserCard user={user} onFollow={follow} />;
};
6. 提供者(Context) ⭐⭐⭐⭐
使用 createContext + useContext 管理全局或局部状态。完整详情请参阅 react-usecontext 技能。
7. 渲染属性 ⭐⭐
传入一个函数作为属性——组件会用数据调用该函数:
<WindowSize render={({ width }) => (
width < 768 ? <MobileMenu /> : <DesktopMenu />
)} />
适用于反向数据流(子组件 → 父组件)。2025 年,这种模式大多已被自定义 Hook 取代。
8. 函数子元素 ⭐⭐
渲染属性的一种变体——将函数作为 children 传入。由 Formik、Downshift 推广:
<Toggle>
{({ on, toggle }) => (
<button onClick={toggle}>{on ? 'ON' : 'OFF'}</button>
)}
</Toggle>
9. 属性 Getter ⭐⭐
Hook 返回预构建的属性对象,以便展开并应用到元素上。React Hook Form(register)、react-table 都使用此模式:
function useToggle() {
const [on, setOn] = useState(false);
const getTogglerProps = (overrides = {}) => ({
onClick: () => setOn(prev => !prev),
'aria-pressed': on,
...overrides,
});
return { on, getTogglerProps };
}
// Usage: <button {...getTogglerProps({ className: 'my-btn' })}>
10. 错误边界 ⭐⭐
捕获组件树中的 JS 错误,并显示备用 UI。需要使用类组件或 react-error-boundary 库:
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => { /* reset state */ }}>
<Dashboard />
</ErrorBoundary>
使用不足——大多数应用都应按区块设置细粒度的错误边界。
11. Portal ⭐⭐⭐
在 DOM 父级树之外渲染。可解决 z-index、overflow: hidden 和裁剪问题:
import { createPortal } from 'react-dom';
function Modal({ isOpen, children }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay">{children}</div>,
document.body
);
}
12. HOC(高阶组件) ⭐ — 旧模式
接收一个组件并返回新组件的函数。现代代码中已被自定义 Hook 取代。
// Legacy
const ProtectedDashboard = withAuth(Dashboard);
// Modern equivalent
function ProtectedRoute({ children }) {
const { user } = useAuth();
if (!user) return <Navigate to="/login" />;
return children;
}
13. 原子设计 ⭐⭐⭐
层级:原子(按钮)→ 分子(搜索框 = 输入框 + 按钮)→ 有机体(页眉)→ 模板(不含数据的布局)→ 页面(带数据的模板)。非常适用于设计系统和大型团队项目。
参考资料
- 复合模式 — patterns.dev — 通过 Context 实现复合组件的可视化指南
- 使用 React Hooks 构建复合组件 — Kent C. Dodds 的经典教程
- React 设计模式 — Telerik — 现代模式与最佳实践综述