155 lines
5.9 KiB
Plaintext
155 lines
5.9 KiB
Plaintext
---
|
||
title: React 面试设计模式
|
||
description: 探索 React 设计模式,包括高阶组件、渲染道具和容器/展示模式,帮助您构建干净、可重用和可扩展的应用程序
|
||
---
|
||
|
||
React 鼓励基于组件的架构,但随着应用程序的增长,维护干净、可重用和可扩展的代码至关重要。以下是 React 中一些最常见的设计模式以及何时使用它们。
|
||
|
||
## 高阶组件 (HOC)
|
||
|
||
高阶组件 (HOC) 是一个接受组件并返回一个增强组件的函数,该组件具有额外的 props 或逻辑。它促进了跨多个组件的代码重用。
|
||
|
||
```jsx
|
||
function withAuth(Component) {
|
||
return function WrappedComponent(props) {
|
||
const isAuthenticated = localStorage.getItem('token'); // 检查身份验证状态
|
||
return isAuthenticated ? <Component {...props} /> : <p>访问被拒绝</p>;
|
||
};
|
||
}
|
||
|
||
function Dashboard() {
|
||
return <h1>Dashboard</h1>;
|
||
}
|
||
|
||
const ProtectedDashboard = withAuth(Dashboard);
|
||
```
|
||
|
||
HOC 适用于:
|
||
|
||
* 在多个组件之间重用逻辑(例如,身份验证、分析、日志记录、获取数据)
|
||
* 在不修改原始组件的情况下添加行为
|
||
* 无法使用钩子的情况,例如类组件
|
||
|
||
高阶组件在 pre-hooks 时代对于向组件添加功能更有用。现在我们有了 React 钩子以及创建自定义钩子的能力,HOC 已经不再那么普遍。
|
||
|
||
在 react.dev 上进一步阅读:[高阶组件 – React](https://www.patterns.dev/react/hoc-pattern/)
|
||
|
||
## 渲染道具
|
||
|
||
渲染道具涉及将一个渲染元素的函数作为 prop 传递给一个组件。该组件使用某些参数(通常是其自身的状态)调用该 prop。
|
||
|
||
这允许组件的父级根据组件状态进行渲染,同时仍然允许父级从外部自定义行为/外观。
|
||
|
||
```jsx
|
||
function MouseTracker({ render }) {
|
||
const [position, setPosition] = useState({ x: 0, y: 0 });
|
||
|
||
useEffect(() => {
|
||
const handleMouseMove = (event) => {
|
||
setPosition({ x: event.clientX, y: event.clientY });
|
||
};
|
||
window.addEventListener('mousemove', handleMouseMove);
|
||
|
||
return () => window.removeEventListener('mousemove', handleMouseMove);
|
||
}, []);
|
||
|
||
return render(position);
|
||
}
|
||
|
||
function App() {
|
||
return (
|
||
<MouseTracker
|
||
render={(position) => (
|
||
<p>
|
||
Mouse: {position.x}, {position.y}
|
||
</p>
|
||
)}
|
||
/>
|
||
);
|
||
}
|
||
```
|
||
|
||
渲染道具适用于:
|
||
|
||
* 在组件之间共享逻辑,同时保持 UI 灵活性
|
||
* 无法使用钩子的情况,例如类组件
|
||
* 提供逻辑和行为的无头组件,同时允许自定义外观
|
||
|
||
在 react.dev 上进一步阅读:[Render Props - React](https://www.patterns.dev/react/render-props-pattern/)
|
||
|
||
## 容器/展示模式
|
||
|
||
容器/展示模式是 React 中使用的一种设计模式,用于将状态管理(逻辑)与 UI 渲染(展示)分开。它通过确保关注点的明确分离,有助于使组件可重用、可维护和可测试。
|
||
|
||
在客户端,数据可以来自用户的输入、从 API 获取、`localStorage`、WebSockets 等。不假设数据来自哪里是构建组件的好方法。
|
||
|
||
**展示组件:**
|
||
|
||
* 仅专注于渲染 UI
|
||
* 不包含状态(除了本地 UI 状态,如切换)
|
||
* 通过 props 接收所有数据并使用事件处理程序(例如 `onClick`、`onChange`)
|
||
* 可重用且易于测试,因为它们独立于业务逻辑
|
||
* 不假定如何获取数据
|
||
|
||
```jsx
|
||
function UserList({ users }) {
|
||
return (
|
||
<ul>
|
||
{users.map((user) => (
|
||
<li key={user.id}>
|
||
{user.name} - {user.email}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
);
|
||
}
|
||
```
|
||
|
||
**容器组件:**
|
||
|
||
* 管理状态、API 调用和业务逻辑
|
||
* 将数据和函数作为 props 传递给展示组件
|
||
* 不包含 UI 代码(最小 JSX,除了包装展示组件)
|
||
|
||
```jsx
|
||
function UserListContainer() {
|
||
const [users, setUsers] = useState([]);
|
||
|
||
useEffect(() => {
|
||
fetch('https://jsonplaceholder.typicode.com/users')
|
||
.then((res) => res.json())
|
||
.then((data) => setUsers(data));
|
||
}, []);
|
||
|
||
return <UserList users={users} />;
|
||
}
|
||
```
|
||
|
||
这种模式允许:
|
||
|
||
* 使用不同的数据源重用 UI 组件
|
||
* UI 和状态逻辑之间的清晰分离
|
||
* 使 UI 组件更易于测试
|
||
|
||
### 替代方法
|
||
|
||
* **自定义钩子(例如,用于获取用户的 `useUser` 钩子)**:一种更现代的方法,可在多个组件中保持逻辑的可重用性
|
||
* **状态管理库(Redux、Zustand)**:单独处理全局状态,无需显式容器组件
|
||
|
||
在 react.dev 上进一步阅读:[容器/展示模式](https://www.patterns.dev/react/presentational-container-pattern)
|
||
|
||
## 面试需要了解的内容
|
||
|
||
* **首先使用钩子**:虽然 HOC 和 render props 曾经是 React 中代码重用的流行模式,但它们已被钩子取代。但是,它们在某些情况下仍然有用,在某些情况下,仅靠钩子可能不够用或不可用,例如在仍在使用类组件的旧代码库中。
|
||
* **容器/展示超越前端**:容器/展示模式背后的关键思想是将展示与数据源分离。将数据获取与展示分离是一个强大的概念,在前端工程之外也很有用。在构建后端系统时,可以从其他服务获取数据,从数据库加载数据,从文件系统读取数据等。通过进行这种分离,代码将更容易重用和测试。
|
||
|
||
## 练习题
|
||
|
||
**测验:**
|
||
|
||
* [React 中的高阶组件是什么?](/questions/quiz/what-are-higher-order-components-in-react?framework=react\&tab=quiz)
|
||
* [Flux 模式是什么,它有什么好处?](/questions/quiz/what-is-the-flux-pattern-and-what-are-its-benefits?framework=react\&tab=quiz)
|
||
* [解释 React 中的展示组件与容器组件模式](/questions/quiz/explain-the-presentational-vs-container-component-pattern-in-react?framework=react\&tab=quiz)
|
||
* [React 中的 render props 是什么,它们是做什么的?](/questions/quiz/what-are-render-props-in-react-and-what-are-they-for?framework=react\&tab=quiz)
|
||
* [解释 React 中的组合模式](/questions/quiz/explain-the-composition-pattern-in-react?framework=react\&tab=quiz)
|