来源:《高效简洁!React 开发时用到的 11 种设计模式》晨米酱-语雀文档
作为一名React开发者,你可能深知构建用户界面是一件令人兴奋且有趣的事情。但随着项目规模的增长,代码可能会变得混乱且难以维护。这时候,React 设计模式就派上用场了!
在本文中,我们将介绍 11 种重要的设计模式,它们能让你的React代码:
- 更加整洁
- 运行更高效
- 更易于理解
掌握设计模式是成为高级前端工程师的重要一步。
在深入了解这些模式之前,让我们先来理解什么是设计模式,以及为什么要关注它们。
什么是编程中的设计模式?
设计模式是针对常见编程问题的经过验证的解决方案。
设计模式是经过实践验证的、用于解决常见编程问题的方法。与其在编写代码时重复造轮子,不如利用设计模式来高效、可靠地解决问题。你可以将其视为代码的架构蓝图。
设计模式并非简单的代码模板,而是改进工作方式和结构的思路。它们帮助开发者更好地组织项目,并避免常见的陷阱。
为什么要在React中使用设计模式?
使用设计模式非常重要,因为它们能:
- 提高代码可读性:清晰的模式意味着其他开发者(或未来的你)能快速理解代码。
- 减少Bug:结构化的代码降低了出错的风险。
- 提升效率:避免重复解决相同问题。
- 改善协作:共享的模式让团队协作更高效。
- 增强扩展性:当应用程序规模扩大时,设计模式有助于保持代码的有序性。
你可以将设计模式作为代码质量的基准。
了解设计模式的重要性后,接下来我们来看看你应该掌握的11种React设计模式!
11 种 React 设计模式
设计模式 #1:容器组件与展示组件
该模式将应用程序的逻辑(容器)与UI展示(展示组件)分离,提升代码结构性,使每个部分更易于管理。
容器组件与展示组件的定义
- 容器组件处理逻辑和数据获取,不关心UI呈现。
- 展示组件专注于UI展示,通过props接收数据并渲染。
目的
该模式的目标是关注点分离:容器组件负责逻辑,展示组件负责UI,增强代码的可理解性、可测试性和可维护性。
使用技巧
- 保持展示组件简单:它们仅关注显示数据,不涉及数据源。
- 提高UI复用性:展示组件与逻辑解耦,可在不同地方重用。
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 清晰分离逻辑与UI | 可能增加文件和组件数量 |
| 便于单独测试容器和UI组件 | 对简单应用可能显得过度设计 |
| 促进UI组件复用 |
最适用场景
- 中大型应用
- 复杂数据获取的项目
代码示例
展示组件(只负责显示数据)
// UserList.jsx
const UserList = ({ users }) => (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
export default UserList;
容器组件(负责数据获取)
// UserListContainer.jsx
import { useEffect, useState } from 'react';
import UserList from './UserList';
const UserListContainer = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers);
}, []);
return <UserList users={users} />;
};
export default UserListContainer;
设计模式 #2:自定义 Hooks
自定义 Hooks 使得在 React 组件中提取和重用状态逻辑成为可能。通过将逻辑封装成可复用的函数,可以避免在多个组件中重复相同的代码。
为什么使用自定义 Hooks
当多个组件共享相同的逻辑(如数据获取、表单处理等)时,自定义 Hooks 让这些逻辑抽象并重复使用。
命名约定
自定义 Hooks 以 use 开头,遵循 React 内置 Hooks(如 useState、useEffect)的命名约定。
示例:useDataFetch()
目的
自定义 Hooks 的目的是通过重用状态逻辑实现代码的 DRY(Don't Repeat Yourself)原则,从而保持组件简洁、专注、易于理解。
使用技巧
- 保持专注:自定义 Hooks 应解决特定问题(如数据获取、表单处理)
- 返回必要内容:只返回组件真正需要的数据和函数
- 内部使用其他 Hooks:自定义 Hooks 可以调用 React 内置的
useState、useEffect,甚至其他自定义 Hooks
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 减少代码重复 | 过度使用可能使代码难以理解 |
| 组件保持简洁专注 | |
| 易于测试和重用 |
最适用场景
- 需要涉及状态或副作用的可重用逻辑
- 数据获取、身份验证、表单处理等场景
代码示例
// useFetch.js
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
});
}, [url]);
return { data, loading, error };
};
export default useFetch;
// 使用自定义 Hook 的组件
import useFetch from './useFetch';
const UserList = () => {
const { data: users, loading, error } = useFetch('https://api.example.com/users');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
export default UserList;
你注意到了吗?在这个代码示例中,我们也使用了设计模式 #1:容器组件和展示组件。
什么时候不应该使用自定义 Hooks
- 某个组件特定的逻辑且难以复用时
- 当引入过多抽象会让代码变得难以理解时
- 如果需要重用 JSX,创建一个组件
- 如果需要重用不涉及 React hooks 的逻辑,创建一个工具函数
- 如果需要重用包含 React hooks 的逻辑,创建一个自定义 Hook
设计模式 #3:复合组件
在 React 中,复合组件是一种设计模式,其中一个组件由多个协同工作的小组件组成。这种模式的理念是创建一个灵活且可重用的组件系统,其中每个子组件具有特定功能,并共同构成一个完整的组件。这就像构建一套设计好的乐高积木,每个积木都有特定功能,但可以灵活组合成各种结构。
现实生活示例
一个典型的复合组件是 <BlogCard>,它通常包含标题、描述、图片和“阅读更多”内容的按钮等子元素。由于博客由多个页面组成,你可能需要根据不同场景以不同方式展示 <BlogCard>。
例如,在搜索结果页面中你可能会选择隐藏图片,而在其他页面中则可能将图片放在标题上方。虽然可以通过 props 和条件渲染来实现,但如果这些定制逻辑过多,代码会变得冗长且难以维护。这时复合组件模式就能派上用场。
使用场景
- 标签页
- 下拉菜单
- 手风琴组件
- 博客和产品卡片
目的
复合组件模式的核心是保持共享状态和行为的同时,提供灵活的组合 UI 元素的能力。
使用技巧
- 父组件管理状态:父组件应管理共享状态,确保各个子组件之间的数据一致性
- 使用 Context 传递状态:对于深层嵌套的组件,React Context 可以简化状态的传递和管理
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 灵活的组件组合方式 | 对初学者来说可能较复杂 |
| 保持组件的封装性 | 深层嵌套时较难理解 |
最适用场景
- 标签页、手风琴、下拉菜单和卡片等 UI 模式
- 需要在多个子组件之间共享状态
代码示例
// ProductCard.jsx
export default function ProductCard({ children }) {
return (
<div className='product-card'>{children}</div>
);
}
ProductCard.Title = ({ title }) => {
return <h2 className='product-title'>{title}</h2>;
};
ProductCard.Image = ({ imageSrc }) => {
return <img className='product-image' src={imageSrc} alt='Product' />;
};
ProductCard.Price = ({ price }) => {
return <p className='product-price'>${price}</p>;
};
ProductCard.Title.displayName = 'ProductCard.Title';
ProductCard.Image.displayName = 'ProductCard.Image';
ProductCard.Price.displayName = 'ProductCard.Price';
// App.jsx
import ProductCard from './components/ProductCard';
export default function App() {
return (
<ProductCard>
<ProductCard.Image imageSrc='https://via.placeholder.com/150' />
<ProductCard.Title title='产品标题' />
<ProductCard.Price price='9.99' />
</ProductCard>
);
}
你可以按任意顺序排列内部组件,灵活定制展示效果。
设计模式 #4:属性组合
属性组合模式允许通过传递不同的 props 组合来修改组件的行为或外观,从而创建组件的变体,而无需重复编写多个版本的组件。
该模式增强了灵活性和定制化,避免了代码库中出现过多相似的组件。
常见使用场景
- 具有不同样式的按钮(如:
primary,secondary,disabled) - 带有可选元素的卡片(如:图片、图标或标题)
目的
属性组合模式的目的是通过简单的方式创建组件的变体,而无需复制代码,使组件保持整洁、易于维护。
使用技巧
- 组合Boolean props:对于简单变体,使用布尔型 props(如:
isPrimary、isDisabled) - 避免过多 props:如果需要过多 props 来控制行为,考虑拆分组件或使用复合组件模式(设计模式 #3)。
使用默认 Props:为 props 设置默认值,优雅处理缺失的 props。
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 减少创建多个相似组件的需求 | 过度使用可能导致"props 爆炸" |
| 易于定制组件的行为和外观 | |
| 保持代码 DRY(不重复自己) |
最适用场景
- 按钮、卡片、提示框等组件
- 具有多个可配置状态的组件
代码示例
假设你要构建一个可以在样式、大小和禁用状态上变化的按钮组件:
// Button.jsx
const Button = ({ type = 'primary', size = 'medium', disabled = false, children, onClick }) => {
let className = `btn ${type} ${size}`;
if (disabled) className += ' disabled';
return (
<button className={className} onClick={onClick} disabled={disabled}>
{children}
</button>
);
};
// App.jsx
import Button from './components/Button';
const App = () => (
<div>
<Button type="primary" size="large" onClick={() => alert('主要按钮')}>
主要按钮
</Button>
<Button type="secondary" size="small" disabled>
禁用的次要按钮
</Button>
<Button type="danger" size="medium">
危险按钮
</Button>
</div>
);
设计模式 #5:受控组件
受控输入是指其值由 React 状态控制的表单元素。在这种模式中,表单输入的值始终与组件的状态保持同步,使 React 成为输入数据的唯一数据源。
该模式常用于输入框、文本区域、复选框和选择框等表单元素。
输入元素的值与 React 的状态绑定。当状态发生变化时,输入框的值会相应更新。
受控组件 VS 非受控组件:
- 受控组件:值由 React 状态控制
- 非受控组件:依赖 DOM 来管理其状态(例如,通过 ref 访问)
目的
受控组件的目的是完全控制表单输入,确保组件行为可预测且一致,特别适用于需要验证输入、应用格式化或动态提交数据的场景。
使用技巧
- 使用 onChange 事件:通过
onChange更新状态,确保输入值与状态同步 - 初始化状态:设置初始状态,避免出现未定义值
- 表单验证:利用受控组件实现实时验证或格式化
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 易于验证和处理输入 | 可能需要更多的样板代码 |
| 使表单元素可预测且易于调试 | 在处理大型表单时可能影响性能 |
| 完全控制用户输入 |
最适用场景
- 需要验证的表单:如实时验证输入字段时
- 动态表单:表单输入依赖于动态数据或逻辑时
- 复杂输入:例如格式化电话号码或电子邮件时
代码示例
import { useState } from 'react';
function MyForm() {
const [name, setName] = useState('');
const handleChange = (e) => {
setName(e.target.value);
};
return (
<form>
<input
type="text"
value={name}
onChange={handleChange}
/>
<p>你的名字是:{name}</p>
</form>
);
}
设计模式 #6:错误边界
错误边界是 React 组件,用于捕获子组件树在渲染、生命周期方法和事件处理器的 JavaScript 错误。它可以防止应用崩溃,并显示一个优雅的回退 UI(fallback ui)来处理错误。
这种模式对于提升 React 应用的健壮性和用户体验至关重要。
目的
错误边界的目的是防止某个组件出错时导致整个应用崩溃,相反,以显示用户友好的回退 UI,确保应用的其余部分继续正常运行。
使用技巧
- 包裹关键组件:在可能失败的组件周围使用错误边界(如第三方集成组件)。
- 记录错误:将错误日志发送至 Sentry 或 LogRocket 等服务以便调试。
- 设计回退 UI:创建清晰的回退 UI,告知用户发生了问题。
优点和缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 防止整个应用崩溃 | 无法捕获事件处理器或异步代码中的错误 |
| 提供回退 UI 以获得更好的用户体验 | |
| 帮助捕获和记录生产环境中的错误 |
最适用场景
- 大型应用:单个组件的错误不应影响整个应用。
- 第三方集成:嵌入的第三方组件可能出现不可预见的错误。
- 复杂 UI 组件:用于动态内容或复杂渲染逻辑的组件。
代码示例
React 本身提供了错误边界的实现,但由于其使用类组件,稍显过时。推荐使用专门的 npm 库:react-error-boundary。
// App.jsx
import { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary fallback={<div>出现了一些问题</div>}>
<App />
</ErrorBoundary>
设计模式 #7:懒加载(代码分割)
懒加载是一种只在需要时加载应用组件或部分内容的技术,与一次性加载所有内容不同。它将代码分割成更小的块,按需加载,从而减少初始加载时间,提升性能。
在 React 中的实现
React 通过 React.lazy() 和 Suspense 实现懒加载:
- React.lazy() :动态导入组件。
- Suspense:包装懒加载的组件,在组件加载时显示一个回退 UI(如加载动画)。
目的
懒加载通过减少初始包大小来优化应用性能,尤其适用于不需要立即加载所有组件的大型应用,带来更快的加载速度。
使用技巧
- 分割路由:对路由使用懒加载,只加载每个页面所需的组件。
- 与错误边界结合:将懒加载与错误边界结合使用,处理加载失败的情况(参见“设计模式 #6:错误边界”)。
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 减少初始加载时间 | 组件加载时会有延迟 |
| 提升大型应用性能 | 需要处理加载状态和错误 |
| 按需加载,节省带宽 | 过度分割代码会增加复杂性 |
最适用场景
- 大型应用:适用于不需要立即加载所有组件的场景。
- 性能关键页面:如仪表板等需要快速加载的页面。
- 第三方组件:可懒加载的第三方组件集成。
代码示例
// Profile.jsx
const Profile = () => {
return <h2>这是个人资料组件!</h2>;
};
export default Profile;
// App.jsx
import { Suspense, lazy } from 'react';
// 懒加载 Profile 组件
const Profile = lazy(() => import('./Profile'));
function App() {
return (
<div>
<h1>欢迎使用我的应用</h1>
{/* Suspense 提供加载状态 UI */}
<Suspense fallback={<div>加载中...</div>}>
<Profile />
</Suspense>
</div>
);
}
export default App;
设计模式 #8:高阶组件(HOC)
高阶组件 接收一个组件作为参数,并返回一个注入了额外数据或功能的增强组件。
HOC 常用于逻辑复用,如身份验证、数据获取或样式注入等。
HOC 的签名
const EnhancedComponent = withSomething(WrappedComponent);
- WrappedComponent:被增强的源组件
- EnhancedComponent:HOC 返回的新组件
命名约定
HOC 通常使用 with 前缀命名,例如 withAuth、withLogging 或 withLoading。
使用技巧
- 保持纯函数:HOC 应保持纯函数,不修改
WrappedComponent。 - 传递 props:始终将 props 传递给
WrappedComponent,确保它能接收所需的所有属性。
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 促进代码复用 | 可能导致“包装地狱”(过多嵌套的 HOC) |
| 保持组件的单一职责 | 多层抽象可能使调试变得复杂 |
最适用场景
- 横切关注点:如认证、日志记录、主题等共享逻辑。
- 可复用增强:多个组件需要相同行为时。
- 复杂应用:需要抽象公共逻辑,提升可读性。
代码示例
以下是为组件添加加载状态的 HOC 示例:
// HOC - withLoading.js
const withLoading = (WrappedComponent) => {
return ({ isLoading, ...props }) => {
if (isLoading) {
return <div>加载中...</div>;
}
return <WrappedComponent {...props} />;
};
};
export default withLoading;
// DataComponent.js
const DataComponent = ({ data }) => {
return <div>数据: {data}</div>;
};
export default DataComponent;
// App.js
import { useState, useEffect } from 'react';
import withLoading from './withLoading';
import DataComponent from './DataComponent';
// 使用 HOC 增强组件
const DataComponentWithLoading = withLoading(DataComponent);
const App = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setData('这是数据!');
setLoading(false);
}, 2000);
}, []);
return (
<div>
<h1>我的应用</h1>
<DataComponentWithLoading isLoading={loading} data={data} />
</div>
);
};
export default App;
设计模式 #9:使用 Reducer 进行状态管理
当应用状态变得复杂时,使用 Reducer 替代 useState 更为高效。Reducer 通过可预测和有组织的方式处理状态更新。
Reducer 本质上是一个函数,接收当前状态和动作,返回新的状态。
基础概念
- Reducer 函数:纯函数,接收状态和动作作为参数,并返回新状态。
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
- Action(动作) :描述状态更新的对象,通常包含
type字段及其他数据(payload)。 - Dispatch(分发) :用于向 reducer 发送动作以触发状态更新。
目的
当状态逻辑变得过于复杂而不适合使用 useState 时,使用 reducer 集中管理状态更新,使代码更易维护、调试和扩展。
使用技巧
- 保持 Reducer 纯净:避免副作用(如 API 调用或异步代码)。
- 使用常量定义动作类型:避免拼写错误。
优缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 简化复杂状态逻辑 | 增加样板代码(动作、分发等) |
| 集中状态更新便于调试 | 对简单状态管理可能过度设计 |
| 使状态转换可预测 | 学习曲线较陡,特别对初学者 |
最适用场景
- 复杂状态逻辑:当状态依赖多个条件时。
- 中大型应用:多个组件共享状态的应用。
代码示例
以下是使用 useReducer 管理状态的计数器示例:
import { useReducer } from 'react';
// 第一步:定义 reducer 函数
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
case 'RESET':
return { count: 0 };
default:
return state;
}
};
// 第二步:定义初始状态
const initialState = { count: 0 };
// 第三步:创建组件
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>计数: {state.count}</h1>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
<button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
</div>
);
};
export default Counter;
在现代 React 开发中,
Redux是基于 reducer 最常用的状态管理库。
设计模式 #10:使用 Provider 进行数据管理(Context API)
Provider 模式在数据管理中非常有用,通过 Context API 在组件树中传递数据,有效解决了 React 开发中常见的 props 层层传递问题。
Provider 允许管理全局状态,并且任何需要状态的组件都可访问。这种模式通过向组件树“提供”数据,避免了 属性传取(通过多层组件传递 props)。
基础概念
- Context:React 提供的特性,用于创建和共享全局状态。
- Provider:为子树中的组件提供数据的组件。
- Consumer:消费 Provider 提供的数据的组件。
- useContext Hook:无需 Consumer,直接访问 context 值。
目的
这种模式通过 Provider 提供全局状态,简化了深层嵌套组件之间的数据共享。它有助于保持代码整洁、可读,并避免不必要的 props 传递。
使用技巧
- 合理使用 Context:适合用于主题、认证或用户设置等全局状态。
- 结合 Reducer:对于复杂状态逻辑,结合 useReducer 使用,获得更好的控制。
- 拆分 Context:避免使用单一的大 context,针对不同数据使用多个小 context。
优点与缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 减少 props 层层传递 | 不适合频繁变化的数据(可能导致不必要的重渲染) |
| 集中数据便于访问 | 如果 context 值频繁变化,可能影响性能 |
| 对中小型应用易于设置 |
最适用场景
- 全局状态管理:如主题、认证状态、语言设置等。
- 避免 Props 层层传递:当数据需要通过多层组件传递时。
- 中等复杂度应用:适合需要简单全局状态管理的应用。
代码示例
以下是使用 ThemeProvider 进行数据管理的示例:
// ThemeContext.jsx
import { createContext, useState } from 'react';
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
// ThemeToggleButton.jsx
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
const ThemeToggleButton = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme}>
切换到{theme === 'light' ? '深色' : '浅色'}模式
</button>
);
};
export default ThemeToggleButton;
// App.js
import { ThemeProvider } from './ThemeContext';
import ThemeToggleButton from './ThemeToggleButton';
const App = () => {
return (
<ThemeProvider>
<div>
<h1>欢迎使用应用</h1>
<ThemeToggleButton />
</div>
</ThemeProvider>
);
};
export default App;
在 React 19 中,你不需要使用
<Context.Provider>,可以直接将<Context>作为 provider 渲染
const ThemeContext = createContext('');
function App({children}) {
return (
<ThemeContext value="dark">
{children}
</ThemeContext>
);
}
设计模式 #11:Portals
Portals 允许你将子元素渲染到父组件层级之外的 DOM 树部分。
这对于渲染需要脱离正常 DOM 流显示的元素(如模态框、工具提示或覆盖层)非常有用。
即使 DOM 父级发生变化,React 组件结构保持不变。
目的
此模式的目的是提供一种在父组件层级之外渲染组件的方法,使得需要打破正常流的 UI 元素易于管理,同时不破坏组件树的主要结构。
使用技巧
- 模态框和覆盖层:Portals 适合用于渲染显示在其他内容之上的元素,如模态框和工具提示。
- 避免过度使用:虽然非常有用,Portals 应该只在必要时使用,因为它可能使组件层级和事件传播变得复杂。
优点与缺点
| 优点 ✅ | 缺点 ❌ |
|---|---|
| 保持组件树整洁,避免布局问题 | 可能使事件传播变复杂(如点击事件不再冒泡) |
最适用场景
- 覆盖层:模态框、工具提示或任何需要显示在其他内容之上的 UI 元素。
- 打破 DOM 流:当元素需要脱离标准组件层级时(如通知)。
代码示例
// Modal.jsx
import { useEffect } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ isOpen, closeModal, children }) => {
// 阻止 body 滚动
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isOpen]);
if (!isOpen) return null;
return ReactDOM.createPortal(
<>
{/* 遮罩层 */}
<div
style={overlayStyles}
onClick={closeModal}
/>
{/* 模态框 */}
<div style={modalStyles}>
{children}
<button onClick={closeModal}>关闭</button>
</div>
</>,
document.getElementById('modal-root')
);
};
const overlayStyles = { /* 样式 */ };
const modalStyles = { /* 样式 */ };
export default Modal;
// App.js
import { useState } from 'react';
import Modal from './Modal';
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<h1>React Portals 示例</h1>
<button onClick={() => setIsModalOpen(true)}>打开模态框</button>
<Modal isOpen={isModalOpen} closeModal={() => setIsModalOpen(false)}>
<h2>模态框内容</h2>
<p>这是模态框的内容</p>
</Modal>
</div>
);
};
export default App;
// index.html
<body>
<div id="root"></div>
<div id="modal-root"></div>
</body>
总结
学习和掌握设计模式是成为高级前端开发者的关键步骤。🆙
这些模式不仅仅是理论,它们解决了状态管理、性能优化和 UI 组件架构等实际问题。
通过在日常工作中应用这些模式,你将能够应对各种开发挑战,创建高性能且易于维护的应用。