React底层常见的设计模式

在React中,常见的设计模式为开发者提供了结构化和可重用的解决方案,有助于提高代码的可维护性和可扩展性。以下是对React中几种常见设计模式的详细解析,并附上示例代码和注释:

1. 容器组件与展示组件模式(Container/Presentational Pattern)

描述

容器组件负责数据获取、状态管理和业务逻辑,而展示组件仅负责渲染UI,不直接管理状态。

示例代码

jsx 复制代码
// 展示组件:TodoItem.js
import React from 'react';

const TodoItem = ({ todo }) => (
  <div>
    <span>{todo.text}</span>
    <button onClick={() => alert(`Completed ${todo.text}`)}>Complete</button>
  </div>
);

export default TodoItem;

// 容器组件:TodoList.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';

class TodoList extends Component {
  state = {
    todos: [
      { id: 1, text: 'Learn React' },
      { id: 2, text: 'Learn Redux' },
    ],
  };

  render() {
    return (
      <div>
        <h1>Todo List</h1>
        <ul>
          {this.state.todos.map(todo => (
            <li key={todo.id}>
              <TodoItem todo={todo} />
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

export default TodoList;

注释

  • TodoItem是一个展示组件,它接收一个todo对象作为props,并渲染出对应的文本和按钮。
  • TodoList是一个容器组件,它管理一个todos状态数组,并在渲染时遍历该数组,为每个todo项渲染一个TodoItem组件。

2. 高阶组件模式(Higher-Order Component Pattern, HOC)

描述

高阶组件是一个函数,它接收一个组件作为参数,并返回一个新的组件。这个新组件可以访问原始组件的props,并可以添加额外的props或行为。

示例代码

jsx 复制代码
// 高阶组件:withLogging.js
import React from 'react';

const withLogging = (WrappedComponent) => {
  return class extends React.Component {
    componentDidMount() {
      console.log(`${WrappedComponent.name} mounted`);
    }

    componentWillUnmount() {
      console.log(`${WrappedComponent.name} will unmount`);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

// 使用高阶组件的组件:EnhancedTodoItem.js
import React from 'react';
import withLogging from './withLogging';
import TodoItem from './TodoItem'; // 假设TodoItem是上面定义的展示组件

// 注意:这里我们实际上是在增强TodoItem组件,但为了示例清晰,我们假设有一个新的组件EnhancedTodoItem
const EnhancedTodoItem = withLogging(TodoItem);

// 通常情况下,你会直接使用EnhancedTodoItem而不是TodoItem
// 但在这个例子中,我们只是为了展示HOC的用法,所以EnhancedTodoItem和TodoItem功能相同,只是多了日志记录。

// 实际上,你可能会在EnhancedTodoItem中添加更多的逻辑或props。
export default EnhancedTodoItem;

注意 :在上面的withLogging示例中,我们实际上没有直接对TodoItem进行增强(因为TodoItem已经是一个纯函数组件,并且没有额外的逻辑需要添加),但为了展示HOC的用法,我们假设有一个新的组件EnhancedTodoItem使用了这个HOC。在实际应用中,你会在HOC中添加额外的逻辑或props,并将其应用于需要增强的组件。

另外,由于TodoItem是一个函数组件,它没有name属性,所以console.log中的${WrappedComponent.name}可能不会显示你期望的名字。在实际应用中,你可能需要为函数组件添加一个displayName静态属性或使用其他方法来标识组件。

修正后的示例 (为函数组件添加displayName):

jsx 复制代码
// TodoItem.js(添加displayName)
import React from 'react';

const TodoItem = ({ todo }) => (
  // ...之前的代码
);

TodoItem.displayName = 'TodoItem'; // 添加displayName以便在日志中正确显示组件名

export default TodoItem;

这样,当使用withLogging高阶组件时,日志中就会正确地显示TodoItem mountedTodoItem will unmount

当然,除了之前提到的容器组件与展示组件模式和高阶组件模式外,React中还有其他常见的设计模式。以下是对这些模式的详细解析,并附上示例代码和注释:

3. 渲染属性模式(Render Props Pattern)

描述

渲染属性模式是一种将函数作为属性传递给组件的技术,该函数返回一个React元素。这种模式允许组件之间共享代码和逻辑。

示例代码

jsx 复制代码
// MouseTracker.js
import React, { useState } from 'react';

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

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

  return (
    <div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
};

export default MouseTracker;

// App.js
import React from 'react';
import MouseTracker from './MouseTracker';

const App = () => (
  <MouseTracker
    render={({ x, y }) => (
      <h1>鼠标的当前位置是 ({x}, {y})</h1>
    )}
  />
);

export default App;

注释

  • MouseTracker组件接收一个render属性,该属性是一个函数,它接收鼠标位置作为参数,并返回一个React元素。
  • App组件中,我们使用MouseTracker组件,并传递一个函数作为render属性,该函数根据鼠标位置渲染一个h1元素。

4. 自定义钩子模式(Custom Hook Pattern)

描述

自定义钩子允许你将组件逻辑提取到可重用的函数中。它们可以让你在不增加组件类的情况下复用状态逻辑。

示例代码

jsx 复制代码
// useFetch.js
import { useState, useEffect } from 'react';

const useFetch = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(response => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.json();
      })
      .then(jsonData => {
        setData(jsonData);
        setLoading(false);
      })
      .catch(error => {
        setError(error);
        setLoading(false);
      });
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

// DataDisplay.js
import React from 'react';
import useFetch from './useFetch';

const DataDisplay = ({ url }) => {
  const { data, loading, error } = useFetch(url);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <pre>
      {JSON.stringify(data, null, 2)}
    </pre>
  );
};

export default DataDisplay;

注释

  • useFetch是一个自定义钩子,它接收一个URL作为参数,并返回一个包含数据、加载状态和错误信息的对象。
  • DataDisplay组件使用useFetch钩子来获取数据,并根据加载状态和错误信息渲染相应的UI。

5. 组合模式(Composite Pattern)

描述

组合模式允许你将对象组合成树形结构以表示"部分-整体"的层次结构。在React中,这通常体现在组件树的设计上。

示例代码(简化版):

jsx 复制代码
// Accordion.js
import React, { useState } from 'react';

const Accordion = ({ children }) => {
  const [activeIndex, setActiveIndex] = useState(null);

  const handleItemClick = (index) => {
    setActiveIndex(index);
  };

  return (
    <div>
      {React.Children.map(children, (child, index) =>
        React.cloneElement(child, {
          isActive: index === activeIndex,
          onItemClick: () => handleItemClick(index),
        })
      )}
    </div>
  );
};

// AccordionItem.js
import React from 'react';

const AccordionItem = ({ title, children, isActive, onItemClick }) => (
  <div>
    <h2 onClick={onItemClick}>{title}</h2>
    {isActive && <div>{children}</div>}
  </div>
);

export { Accordion, AccordionItem };

// App.js
import React from 'react';
import { Accordion, AccordionItem } from './Accordion';

const App = () => (
  <Accordion>
    <AccordionItem title="Item 1">Content 1</AccordionItem>
    <AccordionItem title="Item 2">Content 2</AccordionItem>
    <AccordionItem title="Item 3">Content 3</AccordionItem>
  </Accordion>
);

export default App;

注释

  • Accordion组件接收子组件(AccordionItem组件)作为参数,并管理哪个项目处于活动状态。
  • AccordionItem组件接收标题、子组件、活动状态和点击事件处理器作为props,并根据活动状态渲染内容。
  • App组件中,我们使用Accordion组件,并传递多个AccordionItem组件作为其子组件。

这些设计模式在React开发中非常常见,它们有助于提高代码的可维护性、可扩展性和重用性。

相关推荐
桂月二二28 分钟前
服务端驱动UI架构解析:React Server Components与流式渲染的革命
react.js·ui·架构
轨迹H1 小时前
Jsmoke-一款强大的js检测工具,浏览器部署即用,使用方便且高效
前端·javascript·web安全·网络安全·渗透测试
Au_ust2 小时前
千峰React:案例一
前端·react.js·前端框架
Enti7c2 小时前
datalist 是什么?
javascript
IT、木易2 小时前
大白话React Hooks(如 useState、useEffect)的使用方法与原理
前端·javascript·react.js
千里码!2 小时前
java23种设计模式-解释器模式
java·设计模式·解释器模式
马剑威(威哥爱编程)3 小时前
React Native 核心技术知识点快速入门
javascript·react native·react.js
mqwguardain3 小时前
DOM 事件 & HTML 标签属性速查手册
前端·javascript·python·html
漠月瑾-西安4 小时前
从JSON过滤到编程范式:深入理解JavaScript数据操作
javascript·json
AC-PEACE4 小时前
React 项目创建与文件基础结构关系
前端·javascript·react.js