React 设计模式:实用指南

在这里插入图片描述
React 提供了众多出色的特性以及丰富的设计模式,用于简化开发流程。开发者能够借助 React 组件设计模式,降低开发时间以及编码的工作量。此外,这些模式让 React 开发者能够构建出成果更显著、性能更优越的各类应用程序。

本文将会为您介绍五个基础的 React 组件设计模式,并提供实例来协助您优化您的 React 应用。

文章目录

    • 1. 高阶组件(HOC)模式
    • 2. Provider 模式
    • 3. 容器/表现模式
    • 4. 复合模式
    • 5. Hooks 模式

1. 高阶组件(HOC)模式

随着您的应用规模逐步扩大,可能会出现需要在多个组件之间共享相同组件逻辑的情况。而这正是 HOC 模式所能为您实现的。
HOC 是一个纯粹的 JavaScript 函数,它以一个组件作为参数,并在注入或者添加额外的数据和功能之后,返回另一个组件。从本质上讲,它充当了一个 JavaScript 装饰器函数。HOCs 的基本理念与 React 的特性相符,也就是倾向于组合而非继承。

例如,设想一个我们需要对多个应用组件进行统一风格化的场景。我们能够通过实现一个 HOC ,来避免在本地反复构建样式对象,该 HOC 会将样式应用于传入的组件。

import React from 'react';// HOC
function decoratedComponent(WrappedComponent) {return props => {const style = { padding: '5px', margin: '2px' };return <WrappedComponent style={style} {...props} />;};
}const Button = ({ style }) => <button style={{ ...style, color: 'yellow' }}>这是一个按钮。</button>;
const Text = ({ style }) => <p style={style}>这是文本。</p>;const DecoratedButton = decoratedComponent(Button);
const DecoratedText = decoratedComponent(Text);function App() {return (<><DecoratedButton /><DecoratedText /></>);
}export default App;

在上述代码示例中,我们已经对Button 和Text 组件进行了修改,分别生成了DecoratedButton 和DecoratedText。现在这两个组件都继承了由高阶组件(HOC)decoratedComponent 添加的样式。由于Button 组件已经有一个名为style 的prop,HOC 将覆盖它并附加新的prop。

优点

  • 集中维护:有助于在单一位置维护可重用功能。
  • 代码清晰:通过将所有逻辑整合到一个部分来保持代码的清晰,并实现关注点分离。
  • 减少错误:通过避免代码重复,减少整个应用中意外错误的可能性。

缺点

  • Props 名称冲突:有时会导致props 名称冲突,使得调试和扩展应用更具挑战性,特别是当组合许多共享相同prop 名称的 HOC 时。

2. Provider 模式

在复杂的 React 应用中,经常会出现如何使数据对多个组件可访问的挑战。虽然可以使用props 来传递数据,但在所有组件中访问props 值可能会变得繁琐,导致props 钻取(prop drilling)。

Provider 模式
利用 React Context API,以及在某些情况下使用 Redux,为这一挑战提供了解决方案。这种模式允许开发者将数据存储在中心区域,称为 React 上下文对象或 Redux 存储,消除了props 钻取的需要。

使用 React-Redux 实现 Provider 模式
React-Redux 在应用的顶层使用 Provider 模式,为所有组件提供对 Redux 存储的访问权限。以下代码示例展示了如何设置它。

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';const rootElement = document.getElementById('root');
ReactDOM.render(<Provider store={store}><App /></Provider>,rootElement
);

使用 React Context 的 Provider 模式
在 Redux 可能过于复杂的情况下,可以使用 React 的Context API。例如,如果一个 App 组件有一个数据集需要被深层组件树中的 List、PageHeader 和 Text 组件访问,Context API 可以绕过 props 钻取。

以下代码示例说明了如何创建和提供上下文。

import React, { createContext } from 'react';
import SideMenu from './SideMenu';
import Page from './Page';const DataContext = createContext();function App() {const data = {list: ['项目1', '项目2', '项目3'],text: '你好世界',header: 'WriterGate'}; // 在这里定义你的数据结构。return (<DataContext.Provider value={data}><SideMenu/><Page/></DataContext.Provider>);
}

使用 Context 数据
组件可以使用useContext 钩子访问数据,它允许读取和写入上下文对象中的数据。

以下代码示例供参考。

import React, { useContext } from 'react';const SideMenu = () => <List/>
const Page = () => <div><PageHeader/><Content/></div>function List() {const data = useContext(DataContext);return <span>{data.list}</span>;
}function Text() {const data = useContext(DataContext);return <h1>{data.text}</h1>;
}function PageHeader() {const data = useContext(DataContext);return <div>{data.header}</div>;
}const Content = () => {const data = useContext(DataContext);return <div><Text/></div>;
}

优点

  • 允许将数据发送到多个组件,无需通过组件层次结构。
  • 减少了重构代码时出现不可预见错误的可能性。
  • 消除了 props-drilling,这是一种反模式。
  • 简化了保持某种形式的全局状态,因为组件可以访问它。

缺点

  • 过度使用 Provider 模式可能会导致性能问题,特别是当向多个组件传递不断变化的变量时。然而,在较小的应用中,这不会是一个大问题。

3. 容器/表现模式

React 中的容器/表现模式提供了一种实现关注点分离的方法,有效地将视图与应用逻辑分开。理想情况下,我们需要通过将这个过程分成两部分来实现关注点分离。

表现组件
这些组件专注于如何向用户展示数据。它们通过props 接收数据,并负责以视觉上令人愉悦的方式呈现它,通常带有样式,而不修改数据。

考虑以下示例,它显示了从 API 获取的食物图片。为了实现这一点,我们实现了一个函数组件,该组件通过props 接收数据并相应地呈现它。

import React from "react";export default function FoodImages({ foods }) {return foods.map((food, i) => <img src={food} key={i} alt="食物" />);
}

在这个代码示例中,FoodImages 组件充当表现组件。表现组件保持无状态,除非它们需要 React 状态来渲染 UI。接收到的数据不会被修改。相反,它是从相应的容器组件中检索的。

容器组件
这些组件专注于决定向用户展示什么数据。它们的主要角色是将数据传递给表现组件。容器组件通常不渲染除了与它们的数据相关联的表现组件之外的其他组件。容器组件通常没有任何样式,因为它们的责任在于管理状态和生命周期方法,而不是渲染。

以下代码示例是一个容器组件,它从外部 API 获取图片并将它们传递给表现组件(FoodImages)。

import React from "react";
import FoodImages from "./FoodImages";export default class FoodImagesContainer extends React.Component {constructor() {super();this.state = {foods: []};}componentDidMount() {fetch("http://localhost:4200/api/food/images/random/6").then(res => res.json()).then(({ message }) => this.setState({ foods: message }));}render() {return <FoodImages foods={this.state.foods} />;}
}

优点

  • 实现了关注点分离。
  • 表现组件高度可重用。
  • 由于表现组件不改变应用逻辑,它们的外表可以在不了解源代码的情况下进行修改。
  • 测试表现组件是直接的,因为这些组件根据提供的数据进行渲染。

缺点

  • 容器/表现模式下,无状态函数组件需要被重写为类组件。

4. 复合模式

复合组件是 React 组件模式中的高级模式,它允许构建功能以协作完成任务。它允许许多相互依赖的组件共享状态和处理逻辑,同时协同工作。
这种模式为父组件与其子组件之间的通信提供了一个富有表现力和多功能的 API。此外,它允许父组件隐式地与其子组件共享状态。复合组件模式可以使用 Context API 或 React.cloneElement API 来实现。
以下代码示例展示了如何使用 Context API 实现复合组件模式。

import React, { useState, useContext } from "react";const SelectContext = React.createContext();const Select = ({ children }) => {const [activeOption, setActiveOption] = useState(null);return (<SelectContext.Provider value={{ activeOption, setActiveOption }}>{children}</SelectContext.Provider>);
};const Option = ({ value, children }) => {const context = useContext(SelectContext);if (!context) {throw new Error("Option必须在Select组件内使用。");}const { activeOption, setActiveOption } = context;return (<divstyle={activeOption === value ? { backgroundColor: "black" } : { backgroundColor: "white" }}onClick={() => setActiveOption(value)}><p>{children}</p></div>);
};// 将"Option"作为"Select"的静态属性附加。
Select.Option = Option;export default function App() {return (<Select><Select.Option value="john">John</Select.Option><Select.Option value="bella">Bella</Select.Option></Select>);
}

在上面的示例中,select 组件是一个复合组件。它由多个共享状态和行为的组件组成。我们使用「Select.Option」 =「Option」将「Option」和「Select」组件链接起来。现在,导入「Select」组件会自动包含「Option」组件。

优点

  • 复合组件维护其内部状态,这些状态在子组件之间共享。因此,在使用复合组件时无需显式管理状态。
  • 无需手动导入子组件。

缺点

  • 使用「React.Children.map」传递值时,组件堆叠受到限制。只有父组件的直接子组件才能访问 props。
  • 如果现有的 prop 与提供给「React.cloneElement」方法的 props 同名,可能会出现命名冲突。

5. Hooks 模式

React Hooks API 在 React 16.8 中引入,从根本上改变了我们处理 React 组件设计的方式。Hooks 是为了解决 React 开发者遇到的常见问题而开发的。它们通过允许函数组件访问状态、生命周期方法、上下文和 refs 等特性,彻底改变了我们编写 React 组件的方式,这些特性以前是类组件独有的。

useState
useState 钩子使得函数组件能够添加状态。它返回一个包含两个元素的数组:当前状态值和允许你更新它的函数。

import React, { useState } from "react";function ToggleButton() {const [isToggled, setIsToggled] = useState(false);const toggle = () => {setIsToggled(!isToggled);};return (<div><p>切换状态:{isToggled ? "ON" : "OFF"}</p><button onClick={toggle}>{isToggled ? "关闭" : "开启"}</button></div>);
}export default ToggleButton;

useEffect
useEffect 钩子便于在函数组件中执行副作用。它类似于类组件中的「componentDidMount」、「componentDidUpdate」和「componentWillUnmount」的组合。

import React, { useState, useEffect } from "react";function Example() {const [data, setData] = useState(null);useEffect(() => {const fetchData = async () => {try {const response = await fetch("https://jsonplaceholder.typicode.com/posts");const jsonData = await response.json();setData(jsonData);} catch (error) {console.error("获取数据错误:", error);}};fetchData();}, []);return (<div>{data ? (<div><h2>数据获取成功!</h2><ul>{data.map((item, index) => (<li key={index}>{JSON.stringify(item)}</li>))}</ul></div>) : (<p>正在加载数据...</p>)}</div>);
}export default Example;

useRef

useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传递的参数。这个对象在整个组件的生命周期内持续存在。

import React, { useRef } from "react";function InputWithFocusButton() {const inputRef = useRef(null);const focusInput = () => {inputRef.current.focus();};return (<div><input ref={inputRef} type="text" /><button onClick={focusInput}>聚焦输入</button></div>);
}export default InputWithFocusButton;

优点

  • 组织代码,使其整洁清晰,不像生命周期方法那样。
  • 克服了维护挑战,利用热重载和压缩问题。
  • 允许在不编写类的情况下利用状态和其他 React 功能。
  • 促进了跨组件重用有状态逻辑,减少代码重复。
  • 减少了错误的可能性,并使用简单函数实现组合。

缺点

  • 需要遵守特定规则,尽管没有 linter 插件很难识别规则违规。
  • 需要实践才能有效使用某些钩子(例如,useEffect)。
  • 需要小心避免不当使用(例如,useCallback,useMemo)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/13418.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vscode 如何通过Continue引入AI 助手deepseek

第一步&#xff1a; 在deepseek 官网上注册账号&#xff0c;得到APIKeys(deepseek官网地址) 创建属于自己的APIKey,然后复制这个key,(注意保存自己的key)! 第二步&#xff1a; 打开vscode,在插件市场安装Continue插件, 点击设置&#xff0c;添加deepseek模型&#xff0c;默认…

LPJ-GUESS模型入门(一)

一、模型简介 LPJ-GUESS是一个基于过程的动态植被陆地生态系统模型&#xff0c;专为区域或全球研究而设计。这种模型通常被称为动态全球植被模型&#xff08;DGVM&#xff09;。根据区域气候条件和大气二氧化碳浓度的数据&#xff0c;它可以预测地球主要气候带本土生态系统的结…

Windows本地部署DeepSeek-R1大模型并使用web界面远程交互

文章目录 前言1. 安装Ollama2. 安装DeepSeek-r1模型3. 安装图形化界面3.1 Windows系统安装Docker3.2 Docker部署Open WebUI3.3 添加Deepseek模型 4. 安装内网穿透工具5. 配置固定公网地址 前言 最近爆火的国产AI大模型Deepseek详细大家都不陌生&#xff0c;不过除了在手机上安…

MySQL时间类型相关总结(DATETIME, TIMESTAMP, DATE, TIME, YEAR)

MySQL时间类型相关总结(DATETIME, TIMESTAMP, DATE, TIME, YEAR) MySQL官方文档&#xff1a; https://dev.mysql.com/doc/refman/8.0/en/date-and-time-types.html 一. 对比&#xff1a; 在 MySQL 中&#xff0c;处理时间相关的数据类型主要有以下几种&#xff1a;DATE、TIME、…

Ubuntu部署Deepseek-R1模型(8b)

安装ubuntu系统 本机电脑系统ubuntu-20.04 #升级软件 sudo apt-get update#安装curl sudo apt-get install curl通过以上两条指令&#xff0c;完成了curl命令的安装。 安装ollama 打开Ollama官网 选择Linux&#xff0c; 给出如上图方框所示的一条指令 curl -fsSL https:…

【教程】docker升级镜像

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 自动升级 手动升级 无论哪种方式&#xff0c;最重要的是一定要通过-v参数做数据的持久化&#xff01; 自动升级 使用watchtower&#xff0c;可…

4 前端前置技术(上):AJAX技术、Axios技术(前端发送请求)

文章目录 前言一、Ajax技术&#xff08;从服务端获取数据&#xff0c;发送各种请求&#xff09;0 接口文档管理&#xff1a;使用apipost等接口测试软件创建接口便于前端后端分离测试1 基本概念2 原生Ajax使用示例&#xff08;几年前的早期用法&#xff09; 二、 Axios技术(对原…

Google C++ Style / 谷歌C++开源风格

文章目录 前言1. 头文件1.1 自给自足的头文件1.2 #define 防护符1.3 导入你的依赖1.4 前向声明1.5 内联函数1.6 #include 的路径及顺序 2. 作用域2.1 命名空间2.2 内部链接2.3 非成员函数、静态成员函数和全局函数2.4 局部变量2.5 静态和全局变量2.6 thread_local 变量 3. 类3.…

在 Mac M2 上安装 PyTorch 并启用 MPS 加速的详细教程与性能对比

1. 安装torch 在官网上可以查看安装教程&#xff0c;Start Locally | PyTorch 作者安装了目前最新的torch版本2.5.1&#xff0c;需要提前安装python3.9及以上版本&#xff0c;作者python版本是python3.11最新版本 使用conda安装torch&#xff0c;在终端进入要安装的环境&…

股指入门:股指期货是什么意思?在哪里可以做股指期货交易?

股指期货是一种以股票指数为标的物的期货合约&#xff0c;也可以称为股票指数期货或期指。 股指期货是什么意思&#xff1f; 股指期货是一种金融衍生品&#xff0c;其标的资产是股票市场上的股指&#xff0c;例如标普500指数、道琼斯工业平均指数、上证50指数等。 股指期货允…

ZooKeeper单节点详细部署流程

ZooKeeper单节点详细部署流程 文章目录 ZooKeeper单节点详细部署流程 一.下载稳定版本**ZooKeeper**二进制安装包二.安装并启动**ZooKeeper**1.安装**ZooKeeper**2.配置并启动**ZooKeeper** ZooKeeper 版本与 JDK 兼容性3.检查启动状态4.配置环境变量 三.可视化工具管理**Zooke…

【高级篇 / IPv6】(7.2) ❀ 04. 在60E上配置ADSL拨号宽带上网(IPv4) ❀ FortiGate 防火墙

【简介】除了单位用户以外&#xff0c;大部分个人用户目前使用的仍然是30E、50E、60E系列防火墙&#xff0c;固件无法达到目前最高版本7.6&#xff0c;这里以最常用的60E为例&#xff0c;演示固件版本7.2下实现ADSL拨号宽带的IPv6上网。由于内容比较多&#xff0c;文章分上、下…

51单片机07 串口通信

串口是一种应用十分广泛的通讯接口&#xff0c;串口成本低、容易使用、通信线路简单&#xff0c;可实现两个设备的互相通信。单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信。51单片机内部自带UART&#xff08;Universal Asynchronous Recei…

【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信

Kubernetes中Pod间的通信 本系列文章共3篇: 【Kubernetes Pod间通信-第1篇】在单个子网中使用underlay网络实现Pod到Pod的通信【Kubernetes Pod间通信-第2篇】使用BGP实现Pod到Pod的通信(本文介绍)【Kubernetes Pod间通信-第3篇】Kubernetes中Pod与ClusterIP服务之间的通信…

DeepSeek私有化本地部署图文(Win+Mac)

目录 一、DeepSeek私有化本地部署【Windows】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 a、直接访问 b、chatbox网页访问 二、DeepSeek本地部署【Mac】 1、安装Ollama 2、配置环境变量 3、下载模型 4、使用示例 5、删除已下载的模型 三、DeepSeek…

点(线)集最小包围外轮廓效果赏析

“ 图像、点集、线集合最小外轮廓计算应用较为广泛&#xff0c;如抠图、神奇选择、LOD、碰撞检查等领域&#xff0c;提高场景效率” 1.前言 作者基于递归迭代求解实现点集的最小外轮廓计算&#xff0c;在CGLib库中实现&#xff0c;已集成于CGViewer&#xff0c;可联系作者试用&…

博客园-awescnb插件-geek皮肤优化-Markdown样式支持

&#x1f496;简介 博客园-awescnb插件-geek皮肤下&#xff0c;Markdown语法中对部分样式未正常支持&#xff0c;可以通过自定义CSS进行完善。 ✨定义列表 定义自定义CSS 博客园->管理->设置->页面定制 CSS 代码 添加代码 /* 定义列表 */ dl dt{font-size: 14px;f…

接口测试与常用接口测试工具

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接口&#xff0c;一种是系统对外的接口。 系统对外的接口&#xff…

三维粒子滤波(Particle Filter)MATLAB例程,估计三维空间中匀速运动目标的位置(x, y, z),提供下载链接

三维粒子滤波(Particle Filter)MATLAB例程,估计三维空间中匀速运动目标的位置(x, y, z) 文章目录 介绍功能运行结果代码介绍 本 MATLAB 代码实现了三维粒子滤波( P a r t i c l e F i l t e

语言月赛 202311【基因】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202311] 基因 题目描述 有一个长度为 n n n 的字符串 S S S。其只包含有大写字母。 小 A 将 S S S 进行翻转后&#xff0c;得到另一个字符串 S ′ S S′。两个字符串 S S S 与 S ′ S S′ 对应配对。例如说&#xff0c;对…