React 组件设计模式 - 第一部分

2025-06-05

React 组件设计模式 - 第一部分

React 设计模式是开发者在使用 React 构建应用程序时,用来解决常见问题和挑战的既定实践和解决方案。这些模式封装了可复用的解决方案,以解决反复出现的设计问题,从而提升了可维护性、可扩展性和效率。它们提供了一种结构化的方法来组织组件、管理状态、处理数据流和优化性能。

请将以下内容视为前 6 种 React 设计模式:

  1. 容器和展示模式
  2. HOC(高阶组件)模式
  3. 复合组件模式
  4. 提供者模式(使用提供者进行数据管理)
  5. 状态减速器模式
  6. 组件组合模式

1. 容器和展示模式

在此模式中,容器组件负责管理数据和状态逻辑。它们从外部源获取数据,根据需要进行操作,然后将其作为 props 传递给展示性组件。它们通常连接到外部服务、Redux 存储或上下文提供程序。

另一方面,展示性组件专注于 UI 元素的呈现。它们通过 props 从容器组件接收数据,并以视觉上吸引人的方式进行渲染。展示性组件通常是无状态的函数式组件或纯组件,这使得它们更易于测试和复用。

让我们考虑一个复杂的例子来说明这些模式:

假设我们正在构建一个社交媒体仪表盘,用户可以在其中查看好友的帖子并与他们互动。以下是我们的组件结构:

容器组件(FriendFeedContainer):
该组件负责通过 API 获取好友动态数据,处理必要的数据转换,并管理动态状态。它会将相关数据传递给展示组件。

import React, { useState, useEffect } from 'react';
import FriendFeed from './FriendFeed';

const FriendFeedContainer = () => {
  const [friendPosts, setFriendPosts] = useState([]);

  useEffect(() => {
    // Fetch friend posts from API
    const fetchFriendPosts = async () => {
      const posts = await fetch('https://api.example.com/friend-posts');
      const data = await posts.json();
      setFriendPosts(data);
    };
    fetchFriendPosts();
  }, []);

  return <FriendFeed posts={friendPosts} />;
};

export default FriendFeedContainer;

Enter fullscreen mode Exit fullscreen mode

展示组件(FriendFeed):
该组件将从其父容器组件(FriendFeedContainer)接收好友帖子数据作为道具,并以视觉上吸引人的方式呈现它们。

import React from 'react';

const FriendFeed = ({ posts }) => {
  return (
    <div>
      <h2>Friend Feed</h2>
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            <p>{post.content}</p>
            <p>Posted by: {post.author}</p>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default FriendFeed;

Enter fullscreen mode Exit fullscreen mode

通过这种方式构建组件,我们将数据获取和状态管理的关注点与 UI 渲染逻辑分离开来。这种分离使得我们的 React 应用程序在扩展时更容易测试、复用和维护。

2.HOC(高阶组件)模式

高阶组件 (HOC) 是 React 中的一种模式,允许你在多个组件之间复用组件逻辑。它们是一个函数,接受一个组件作为参数,并返回一个具有附加功能的新组件。

为了演示如何在社交媒体仪表盘示例中使用 React Hooks 来使用 HOC,我们假设一个场景:你有多个组件需要从 API 获取用户数据。与其在每个组件中重复获取逻辑,不如创建一个 HOC 来处理数据获取,并将获取的数据作为 props 传递给包装的组件。

这是一个基本的例子:

import React, { useState, useEffect } from 'react';

// Define a higher-order component for fetching user data
const withUserData = (WrappedComponent) => {
  return (props) => {
    const [userData, setUserData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
      // Simulate fetching user data from an API
      const fetchData = async () => {
        try {
          const response = await fetch('https://api.example.com/user');
          const data = await response.json();
          setUserData(data);
          setLoading(false);
        } catch (error) {
          console.error('Error fetching user data:', error);
          setLoading(false);
        }
      };

      fetchData();
    }, []);

    return (
      <div>
        {loading ? (
          <p>Loading...</p>
        ) : (
          <WrappedComponent {...props} userData={userData} />
        )}
      </div>
    );
  };
};

// Create a component to display user data
const UserProfile = ({ userData }) => {
  return (
    <div>
      <h2>User Profile</h2>
      {userData && (
        <div>
          <p>Name: {userData.name}</p>
          <p>Email: {userData.email}</p>
          {/* Additional user data fields */}
        </div>
      )}
    </div>
  );
};

// Wrap the UserProfile component with the withUserData HOC
const UserProfileWithUserData = withUserData(UserProfile);

// Main component where you can render the wrapped component
const SocialMediaDashboard = () => {
  return (
    <div>
      <h1>Social Media Dashboard</h1>
      <UserProfileWithUserData />
    </div>
  );
};

export default SocialMediaDashboard;

Enter fullscreen mode Exit fullscreen mode

在此示例中:

  • withUserData是一个高阶组件,用于处理从 API 获取用户数据。它包装传递的组件 ( WrappedComponent),并将获取到的用户数据作为 prop ( userData) 传递给组件。
  • UserProfile是一个接收userDataprop 并显示用户个人资料信息的功能组件。
  • UserProfileWithUserDataUserProfile是通过包装返回的组件withUserData
  • SocialMediaDashboard是您可以渲染的主要组件UserProfileWithUserData或任何其他需要用户数据的组件。

使用此模式,您可以轻松地在社交媒体仪表板应用程序中的多个组件中重用数据获取逻辑,而无需重复代码。

3. 复合组件模式

React 中的复合组件模式是一种设计模式,它允许您创建协同工作以形成有凝聚力的 UI 的组件,同时仍然保持清晰的关注点分离并提供自定义组件行为和外观的灵活性。

在此模式中,父组件充当一个或多个子组件(称为“复合组件”)的容器。这些子组件协同工作以实现特定的功能或行为。复合组件的关键特性是它们通过其父组件相互共享状态和功能。

下面是使用 hooks 在 React 中实现复合组件模式的简单示例:

import React, { useState } from 'react';

// Parent component that holds the compound components
const Toggle = ({ children }) => {
  const [isOn, setIsOn] = useState(false);

  // Function to toggle the state
  const toggle = () => {
    setIsOn((prevIsOn) => !prevIsOn);
  };

  // Clone the children and pass the toggle function and state to them
  const childrenWithProps = React.Children.map(children, (child) => {
    if (React.isValidElement(child)) {
      return React.cloneElement(child, { isOn, toggle });
    }
    return child;
  });

  return <div>{childrenWithProps}</div>;
};

// Child component for the toggle button
const ToggleButton = ({ isOn, toggle }) => {
  return (
    <button onClick={toggle}>
      {isOn ? 'Turn Off' : 'Turn On'}
    </button>
  );
};

// Child component for the toggle status
const ToggleStatus = ({ isOn }) => {
  return <p>The toggle is {isOn ? 'on' : 'off'}.</p>;
};

// Main component where you use the compound components
const App = () => {
  return (
    <Toggle>
      <ToggleStatus />
      <ToggleButton />
    </Toggle>
  );
};

export default App;

Enter fullscreen mode Exit fullscreen mode

在此示例中:

  • ToggleToggleButton是包含复合组件(和)的父组件ToggleStatus
  • ToggleButton是负责渲染切换按钮的子组件。
  • ToggleStatus是另一个负责显示切换状态的子组件。
  • Toggle组件管理状态(isOn),并提供toggle控制状态的函数。它克隆其子组件,并将isOn状态和toggle函数作为 props 传递给它们。

通过使用复合组件模式,您可以创建可重用和可组合的组件,封装复杂的 UI 逻辑,同时仍允许定制和灵活性。

4. 提供者模式(使用提供者进行数据管理)

React 中的 Provider 模式是一种用于跨多个组件管理和共享应用程序状态或数据的设计模式。它涉及创建一个 Provider 组件,该组件封装状态或数据,并通过 React 的 context API 将其提供给其后代组件。

让我们通过一个示例来说明 React 中用于管理用户身份验证数据的提供程序模式:

// UserContext.js
import React, { createContext, useState } from 'react';

// Create a context for user data
const UserContext = createContext();

// Provider component
export const UserProvider = ({ children }) => {
  const [user, setUser] = useState(null);

  // Function to login user
  const login = (userData) => {
    setUser(userData);
  };

  // Function to logout user
  const logout = () => {
    setUser(null);
  };

  return (
    <UserContext.Provider value={{ user, login, logout }}>
      {children}
    </UserContext.Provider>
  );
};

export default UserContext;

Enter fullscreen mode Exit fullscreen mode

在此示例中:

  • 我们UserContext使用 React 的createContext函数创建了一个名为 的上下文。此上下文将用于跨组件共享用户数据和身份验证相关的函数。
  • 我们定义一个UserProvider组件作为 的提供者UserContext。该组件使用钩子管理用户状态useState,并提供login和等方法logout来更新用户状态。
  • 在里面UserProvider,我们用 包装childrenUserContext.Provider并将user状态和loginlogout函数作为提供者的值传递。
  • 现在,任何需要访问用户数据或身份验证相关功能的组件都可以UserContext使用该useContext钩子。

让我们创建一个从上下文中使用用户数据的组件:

// UserProfile.js
import React, { useContext } from 'react';
import UserContext from './UserContext';

const UserProfile = () => {
  const { user, logout } = useContext(UserContext);

  return (
    <div>
      {user ? (
        <div>
          <h2>Welcome, {user.username}!</h2>
          <button onClick={logout}>Logout</button>
        </div>
      ) : (
        <div>
          <h2>Please log in</h2>
        </div>
      )}
    </div>
  );
};

export default UserProfile;
Enter fullscreen mode Exit fullscreen mode

在此组件中:

我们导入UserContext并使用useContext钩子来访问用户数据和logout提供的功能UserProvider
根据用户是否登录,我们渲染不同的 UI 元素。

最后,我们用以下方式包装我们的应用程序,UserProvider以使所有组件都可以使用用户数据和与身份验证相关的功能:

// App.js
import React from 'react';
import { UserProvider } from './UserContext';
import UserProfile from './UserProfile';

const App = () => {
  return (
    <UserProvider>
      <div>
        <h1>My App</h1>
        <UserProfile />
      </div>
    </UserProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

通过这种方式,提供者模式允许我们跨多个组件管理和共享应用程序状态或数据,而无需进行 prop 钻取,从而使我们的代码更清晰、更易于维护。

文章来源:https://dev.to/fpaghar/react-component-design-patterns-part-1-5f0g
PREV
使用 WSL2 将 Windows 10 用作 Linux 窗口管理器
NEXT
GitHub 资源可提升你的 JavaScript 编码技能