React Design Patterns

React Design Patterns

在使用 React 开发应用程序时,我确信您遇到过不同的用例,而使用相同的旧方法很难解决这些用例。

每个用例都需要不同的模式来以最优化的方式解决问题,并使我们的代码更具可扩展性。

React 提供了不同的设计模式来解决常见问题。 React 中常用的设计模式列表是:

  • Presentational & Container Component
  • Higher Order Components
  • Render Props
  • Compound Components
  • Hooks

让我们一一探讨一下。

1. Presentational & Container Component

React 中的每个组件都有两部分:一个是处理数据的逻辑,另一个是在屏幕上显示数据。 此模式通过将组件分为两类来帮助分离关注点。容器组件处理逻辑和状态管理,而展示组件则专注于根据提供的 props 渲染 UI。

export const DisplayList = ({ patientRecord }) => {
  return (
    <ul>
      {patientRecord.map((patient, id) => {
        return <li key={id}>{patient}</li>;
      })}
    </ul>
  );
};

另一方面,容器组件跟踪内部状态和数据获取逻辑并处理业务逻辑。

export const PatientComponent = () => {
  const [patientList, getPatientList] = React.useState([]);

  React.useEffect(() => {
    // Fetch data through an API and apply business logic
    (async () => {
      const response = await fetchPatientList();

      setPatientList([...response]);
    })()
  }, []);

  // provide data to presentational component to display
  return <DisplayList patientRecord={patientList} />; 
};

容器组件将状态和任何所需的回调作为 props 传递给展示组件。展示组件负责根据提供的 props 渲染 UI,而不用担心数据获取或业务逻辑。

2.Higher Order Components

HOC 将一个组件作为输入,并提供一个新创建的具有附加功能的组件作为输出。它允许您通过使用附加功能包装组件来重用组件逻辑。 通过使用 HOC,我们可以轻松地向多个组件添加通用功能,而无需重复代码。

import React from 'react';

function withLogging(WrappedComponent) {
  return function WithLogging(props) {
    React.useEffect(() => {
      console.log(`Component ${WrappedComponent.name} mounted`);
      return () => {
        console.log(`Component ${WrappedComponent.name} unmounted`);
      };
    }, []);

    return <WrappedComponent {...props} />;
  };
}

// Usage
const MyComponent = (props) => {
  return <div>My Component</div>;
};

const EnhancedComponent = withLogger(MyComponent);

在上面的示例中,withLogger 是一个 HOC,它将组件 (WrappedComponent) 作为输入并返回一个新组件 (WithLogger)。返回的组件在安装和卸载时会向控制台记录一条消息。

为了使用 HOC,我们用 withLogger 包装原始组件 MyComponent,创建一个新组件 EnhancedComponent。增强的组件具有与 MyComponent 相同的功能,但它还包括 HOC 中定义的日志记录行为。

3. Render props

在此模式中,组件接收一个函数作为 prop,并使用该函数来呈现其内容。

import React from 'react';

const MouseTracker = (props) => {
  const [position, setPosition] = React.useState({ x: 0, y: 0 });

  const handleMouseMove = (event) => {
    setPosition({ x: event.clientX, y: event.clientY });
  };

  return <div onMouseMove={handleMouseMove}>{props.renderPosition(position)}</div>;
};

// Usage
const DisplayMousePosition = () => (
  <MouseTracker
    renderPosition={({ x, y }) => (
      <div>
        Mouse position: {x}, {y}
      </div>
    )}
  />
);

像 renderPosition 这样的 prop 被称为 render prop,因为它是指定如何渲染用户界面的一部分的 prop。

4. Compound Components

复合组件是 React 中使用的一种模式,用于创建作为一个组一起工作的组件,允许用户自定义和控制多个相关组件的渲染。

它允许您定义封装子组件的行为和状态的父组件,同时仍然为用户提供确定子组件的组成和外观的灵活性。

让我们考虑一个用例,您需要在 React 中创建一个 List 组件,它看起来像原生 HTML 或 antd 的 Collapse组件

<List>
 <ListItem>1</ListItem>
 <ListItem>2</ListItem>  
 <ListItem>3</ListItem>
 <ListItem>4</ListItem> 
</List>

现在在这种情况下,我们需要将 List 组件的一些状态传输到其子组件(即 ListItem)。它可以通过使用 context API 或 React.cloneElement API 来实现。

让我们探讨一下如何使用 Context API 开发相同的场景。

const ListContext = React.createContext();
const { Provider } = ListContext;

// Parent Component
export const List = (props) => {
  const { children, size } = props;

  const sharedProp = { size }

  return (
    <Provider value={sharedProp}>
      <ul>
        {children}
      </ul>  
    </Provider>
  )
}

// Child Component
export const ListItem = (props) => {
  const contextProp = React.useContext(ListContext);
  const { size } = contextProp;

  return (
   <li className={size === 'medium' ? bg-primary : bg-secondary}>
      {props.children}
   </li>
  )
}

// Usage
<List size='medium'>
 <ListItem>Item 1</ListItem>
 <ListItem>Item 2</ListItem>
 <ListItem>Item 3</ListItem>
</List>

在上面的示例中,创建了 React Context 以在parent()和child()组件之间共享 prop 大小。此外,用户还可以灵活地在每个列表项内提供自定义内容。

5. Hooks

React 函数组件中引入了 Hooks 作为类组件的替代品。有多个钩子可用于管理 React 的状态和不同的生命周期方法

import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <Button onClick={() => setCount(count + 1)}></Button>
  )
}

我们还可以创建自定义挂钩来解决我们的特定用例和代码可重用性。

原文链接:https://juejin.cn/post/7256582988489687077 作者:九殇

(0)
上一篇 2023年7月18日 上午10:16
下一篇 2023年7月18日 上午10:26

相关推荐

发表回复

登录后才能评论