每个开发人员都应了解的 React 模式

React 是一个用于构建用户界面的功能强大的库,随着时间的推移,已经出现了一些模式来帮助开发人员编写更简洁、更易维护的代码。在本博客中,我们将探讨 React 的六种基本模式:

  1. 容器/呈现模式
  2. 高级组件 (HOC)
  3. 渲染道具模式
  4. 钩子模式
  5. 提供程序模式
  6. 复合模式

每种模式都将通过概述、实际示例和需要考虑的权衡因素进行解释。

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

Leave a Reply

Your email address will not be published. Required fields are marked *