
每个开发人员都应了解的 React 模式
React 是一个用于构建用户界面的功能强大的库,随着时间的推移,已经出现了一些模式来帮助开发人员编写更简洁、更易维护的代码。在本博客中,我们将探讨 React 的六种基本模式:
- 容器/呈现模式
- 高级组件 (HOC)
- 渲染道具模式
- 钩子模式
- 提供程序模式
- 复合模式
每种模式都将通过概述、实际示例和需要考虑的权衡因素进行解释。
1.容器/呈现模式
概述:
容器/呈现模式将组件分为两种类型:容器组件处理逻辑(数据获取、状态管理),而呈现组件则专注于用户界面(事物的外观)。这种分离使代码更易于重用和测试。
举例说明:
想象一下,您正在构建一个用户列表应用程序。
// UserListContainer.jsx (Container Component)
import React, { useState, useEffect } from 'react';
import UserList from './UserList';
function UserListContainer() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('<https://api.example.com/users>')
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return <UserList users={users} />;
}
// UserList.jsx (Presentational Component)
import React from 'react';
function UserList({ users }) {
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
说明
- UserListContainer 获取数据并管理状态。它并不关心列表的外观。
- UserList 获取用户道具并渲染它。它可以重复使用,而且不知道数据来自何处。
折衷:
关注点分离- 更易于维护和测试。
✅可重用性- 演示组件可重用。
❌模板- 对于小型应用程序来说,可能会导致额外的文件和复杂性。最终可能会导致组件过多。
2.高级组件 (HOC)
概述:
高阶组件是一个函数,它接收一个组件并返回一个具有附加功能的新组件。这就像包装礼物一样--你原来的组件在不改变代码的情况下获得了额外的功能。
举例说明:
让我们创建一个增加加载逻辑的 HOC。
// withLoading.jsx (HOC)
import React from 'react';
function withLoading(WrappedComponent) {
return function EnhancedComponent({ isLoading, ...props }) {
if (isLoading) return <p>Loading...</p>;
return <WrappedComponent {...props} />;
};
}
// UserProfile.jsx (Component using HOC)
import React from 'react';
import withLoading from './withLoading';
function UserProfile({ user }) {
return <h2>{user.name}</h2>;
}
export default withLoading(UserProfile);
// App.jsx (Usage)
import React, { useState, useEffect } from 'react';
import UserProfile from './UserProfile';
function App() {
const [user, setUser] = useState(null);
useEffect(() => {
setTimeout(() => setUser({ name: 'Alice' }), 2000); // Simulate fetch
}, []);
return <UserProfile isLoading={!user} user={user} />;
}
说明
- withLoading 获取 UserProfile 并添加加载状态检查。
- 如果 isLoading 为真,就会显示 "正在加载...";否则,就会渲染 UserProfile。
折衷:
✅逻辑重用- 避免重复代码。
✅可组合性--可组合多个 HOC。
命名冲突--道具可能会冲突。
❌复杂性--可能增加调试难度。
3.渲染道具模式
概述:
渲染道具模式通过传递一个函数作为道具,让组件共享其逻辑。该函数告诉组件要渲染什么,从而在重复使用逻辑的同时为你提供灵活性。
示例
让我们创建一个跟踪鼠标位置的组件。
// MouseTracker.jsx
import React, { useState } from 'react';
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
// App.jsx (Usage)
import React from 'react';
import MouseTracker from './MouseTracker';
function App() {
return (
<MouseTracker
render={(position) => (
<h3>
Mouse at: ({position.x}, {position.y})
</h3>
)}
/>
);
}
说明
- MouseTracker 管理鼠标的位置状态,并使用该数据调用呈现道具。
- 在 App 中,我们使用位置决定显示什么。
权衡利弊:
✅灵活性- 动态呈现控制。
✅避免 HOC 限制--无道具冲突。
嵌套回调- 可能导致 JSX 杂乱无章。
4.钩子模式
概述:
钩子(在 React 16.8 中引入)可让您在功能组件中使用状态和其他 React 功能。这是一种无需类或 HOC 就能处理逻辑的现代方式。
示例
让我们创建一个用于获取数据的自定义钩子。
// useFetch.js (Custom Hook)
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
// UserCard.jsx (Usage)
import React from 'react';
import { useFetch } from './useFetch';
function UserCard() {
const { data, loading } = useFetch('<https://api.example.com/user/1>');
if (loading) return <p>Loading...</p>;
return <h2>{data.name}</h2>;
}
说明
useFetch
封装了获取数据的逻辑,并返回数据
和加载
。UserCard
使用钩子显示用户名。
权衡利弊:
✅组件更简单- 无类模板。
可重用逻辑- 自定义钩子(如useFetch
)。
学习曲线- 需要了解闭包和依赖关系。
5.提供商模式
概述:
Provider 模式使用 React 的 Context API 在应用程序中共享数据(如主题或用户信息),而无需在每个层级手动传递道具。
举例说明:
让我们跨组件共享一个主题。
const ThemeContext = React.createContext();
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = React.useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
const ThemedComponent = () => {
const { theme, setTheme } = React.useContext(ThemeContext);
return (
<div>
<p>Current Theme: {theme}</p>
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Toggle Theme
</button>
</div>
);
};
const App = () => (
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
);
说明
在这个示例中,我们使用 React Context 提供了一个主题状态,可以在组件树的任何位置访问。ThemeProvider
组件封装了应用程序并提供了主题状态,从而避免了道具钻取。ThemedComponent
使用useContext
获取当前主题,并允许用户在浅色和深色模式之间切换。
权衡利弊:
✅避免道具钻孔- 组件树更整洁。
集中式状态--适合全局数据(如主题、授权)。
过度使用问题--会降低组件的可重用性。
6.复合模式
概述:
复合模式让多个组件作为一个整体协同工作,隐式共享状态。这就像父组件控制着子组件。
举例说明:
让我们制作一个手风琴。
// Accordion.jsx
import React, { useState } from 'react';
function Accordion({ children }) {
const [activeIndex, setActiveIndex] = useState(null);
return React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
isOpen: activeIndex === index,
onToggle: () => setActiveIndex(activeIndex === index ? null : index),
});
});
}
function AccordionItem({ title, isOpen, onToggle, children }) {
return (
<div>
<button onClick={onToggle}>{title}</button>
{isOpen && <div>{children}</div>}
</div>
);
}
// App.jsx (Usage)
import React from 'react';
import Accordion from './Accordion';
import AccordionItem from './AccordionItem';
function App() {
return (
<Accordion>
<AccordionItem title="Section 1">Content 1</AccordionItem>
<AccordionItem title="Section 2">Content 2</AccordionItem>
</Accordion>
);
}
说明
- Accordion 管理打开的项目,并将 isOpen 和 onToggle 传递给其子项目。
- AccordionItem 使用这些道具来显示/隐藏内容。
权衡利弊:
✅封装- 状态由内部管理,使 API 更简洁。
✅灵活性--可以在不破坏逻辑的情况下重新排列组件。
✅更好的语义--模拟类似 HTML 的组合(如<select>
和<option>)
。
❌初始复杂性- 需要精心设计组件。
❌模板略多- 比单片式方法的组件更多。
结论
掌握这些 React 模式将帮助您编写更简洁、更可重用和可扩展的代码。开始将它们集成到您的项目中,随着时间的推移,您将对何时何地有效使用它们有更深入的了解。
0 Comments