mcpField
R
react-design-patterns

react-design-patterns

github.com/b4r7x开发046 次安装12 周前更新
代码仓库

关于 react-design-patterns

面向 React 开发者的设计模式参考 skill,介绍 13 种组件与应用模式,并提供选择指南。建议先从 Custom Hook 入手,再按结构需求采用其他模式。

主要功能

  1. 01提供模式选择指南
  2. 02涵盖 13 种 React 设计模式
  3. 03说明受控与非受控组件
  4. 04介绍逻辑与呈现分离
  5. 05涵盖错误边界与 Portal

适用场景

  1. 01选择可复用组件逻辑的模式
  2. 02设计复合式 Tabs 或 Modal 组件
  3. 03分离数据获取与界面呈现
  4. 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. 原子设计 ⭐⭐⭐

层级:原子(按钮)→ 分子(搜索框 = 输入框 + 按钮)→ 有机体(页眉)→ 模板(不含数据的布局)→ 页面(带数据的模板)。非常适用于设计系统和大型团队项目。

参考资料

评论

支持 Markdown