打破限制!自定义 Hooks 如何提升 React 组件的灵活性
本周开发监控项目,我发现了很多的 React 类组件封装,发现出现了多次UI渲染的情况、代码辨识度也较差,对性能和维护都产生了挑战。这里多个场景的都是状态管理和逻辑复用需求,其实完全没有必要封装类组件。相反我通过引入 React 自定义 Hook,通过逻辑抽离的方式,不仅有效地找到了解决办法,也提高了代码复用性。
背景
通常情况下,我们习惯将业务逻辑和 UI 一起封装到组件中,然而随着项目需求的增加,某些复杂的状态管理和副作用逻辑经常需要在多个组件中复用。传统的组件封装方式在这种场景下复用性较差,且每次使用时都可能引入额外的 UI 代码,增加了维护成本。
为了解决这一问题,我们可以将一些常见的逻辑抽离出来,封装成自定义 Hook,供多个组件复用。下面我们来比较一下不使用自定义 Hooks 和使用自定义 Hooks 的代码。
场景复现对比
1、不使用自定义 Hooks
每个表单组件都需要单独管理 useState
和验证逻辑,代码会变得冗长且难以维护。
import React, { Component } from 'react';class UserForm extends Component {constructor(props) {super(props);this.state = { name: '', workcode: '' };}handleChange = (event) => {this.setState({ [event.target.name]: event.target.value });};handleSubmit = (event) => {event.preventDefault();console.log('handleSubmit:', this.state);};render() {return (<form onSubmit={this.handleSubmit}><inputname="name"type="text"value={this.state.name}onChange={this.handleChange}placeholder="Name"/><inputname="workcode"type="text"value={this.state.workcode}onChange={this.handleChange}placeholder="Workcode"/><button type="submit">Submit</button></form>);}
}class AdminForm extends Component {constructor(props) {super(props);this.state = { name: '', workcode: '' };}handleChange = (event) => {this.setState({ [event.target.name]: event.target.value });};handleSubmit = (event) => {event.preventDefault();console.log('handleSubmit:', this.state);};render() {return (<form onSubmit={this.handleSubmit}><inputname="name"type="text"value={this.state.name}onChange={this.handleChange}placeholder="Name"/><inputname="workcode"type="workcode"value={this.state.workcode}onChange={this.handleChange}placeholder="workcode"/><button type="submit">Submit</button></form>);}
}
这种不断重复地逻辑,每个表单组件都会有类似的状态管理,以及类似的逻辑操作。而且维护成本高,每次新增一个表单,开发者都需要重复编写几乎相同的代码。如果需求或逻辑变更,必须在多个地方修改代码。状态管理和表单验证逻辑完全被分散在各个组件中,不同组件之间无法共享这些逻辑。
2、使用自定义 Hooks
将表单的状态管理和逻辑提取到一个自定义 Hook 中,可以极大地简化代码,并实现逻辑的复用。
我们开发一个 useForm
自定义 Hook,用于管理表单的输入值、以及逻辑等。下面是简化的实现代码:
import React, { useState } from 'react';function useForm(initialValues) {const [values, setValues] = useState(initialValues);const handleChange = (event) => {setValues({ ...values, [event.target.name]: event.target.value });};return { values, handleChange };
}
使用场景:
我们可以在多个表单组件中复用 useForm
,实现输入管理和逻辑的统一处理:
import React, { useState } from 'react';
import { useForm } form './component/customHooks'function UserForm() {const { values, handleChange } = useForm({ name: '', workcode: '' });const handleSubmit = (event) => {event.preventDefault();console.log('handleSubmit:', values);};return (<form onSubmit={handleSubmit}><inputname="name"type="text"value={values.name}onChange={handleChange}placeholder="Name"/><inputname="workcode"type="workcode"value={values.workcode}onChange={handleChange}placeholder="workcode"/><button type="submit">Submit</button></form>);
}function AdminForm() {const { values, handleChange } = useForm({ name: '', workcode: '' });const handleSubmit = (event) => {event.preventDefault();console.log('handleSubmit:', values);};return (<form onSubmit={handleSubmit}><inputname="name"type="text"value={values.name}onChange={handleChange}placeholder="Name"/><inputname="workcode"type="workcode"value={values.workcode}onChange={handleChange}placeholder="workcode"/><button type="submit">Submit</button></form>);
}
通过 useForm
,我们可以轻松管理多个表单的输入状态和逻辑,避免在每个组件中重复编写这些代码。那么自定义 Hook 到底适用哪些适用场景呢?
自定义 Hook 的使用场景
1. 封装通用的 API 逻辑
项目中多个组件需要调用同一 API,并对结果进行状态管理。以往我们需要在每个组件内重复实现状态管理逻辑,而通过自定义 Hook,可以将 API 调用和状态管理逻辑抽离出来,让组件更专注于 UI 渲染。
// 封装通用的 API 请求逻辑
function useFetchData(apiUrl) {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {async function fetchData() {try {const response = await fetch(apiUrl);const result = await response.json();setData(result);} catch (err) {setError(err);} finally {setLoading(false);}}fetchData();}, [apiUrl]);return { data, loading, error };
}// 组件中使用自定义 Hook
function MyComponent() {const { data, loading, error } = useFetchData('/api/data');if (loading) return <div>Loading...</div>;if (error) return <div>Error: {error.message}</div>;return <div>{JSON.stringify(data)}</div>;
}
2. 处理副作用
某些组件需要在不同的生命周期阶段执行异步操作,比如页面加载时请求数据或窗口尺寸变化时调整布局。通过 useEffect
的组合,我们将这些逻辑独立封装在 Hook 中,从而减少组件中的代码。
// 封装窗口尺寸变化的逻辑
function useWindowSize() {const [windowSize, setWindowSize] = useState({width: window.innerWidth,height: window.innerHeight,});useEffect(() => {function handleResize() {setWindowSize({width: window.innerWidth,height: window.innerHeight,});}window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);}, []);return windowSize;
}// 组件中使用自定义 Hook
function LayoutComponent() {const { width, height } = useWindowSize();return <div>Window size: {width} x {height}</div>;
}
简洁高效,复用性还 Good~
总结
通过使用自定义 Hooks,能够有效解决 React 组件中的重复逻辑、状态管理和维护成本的问题。上述例子清晰地展示了从类组件到函数组件的转变,并通过自定义 Hooks 简化了表单处理的逻辑。对于项目,特别是需要频繁维护和扩展的项目,使用好自定义 Hooks 有着显著作用。
相关文章:
打破限制!自定义 Hooks 如何提升 React 组件的灵活性
本周开发监控项目,我发现了很多的 React 类组件封装,发现出现了多次UI渲染的情况、代码辨识度也较差,对性能和维护都产生了挑战。这里多个场景的都是状态管理和逻辑复用需求,其实完全没有必要封装类组件。相反我通过引入 React 自…...

使用arthas测试接口响应时间
一、下载解压 git下载地址: https://github.com/alibaba/arthas/releases 二、启动和选择Java进程 java -jar arthas-boot.jar选择需要测试的java进程,我输入1然后回车 三、使用trace命令 trace 全路径 方法名 trace com.xxx.b2b.mall.goods.service…...
Vue3.x的深度选择器详细解读
在 Vue 3 中,深度选择器(Deep Selector)用于在 <style scoped> 中穿透作用域样式,影响子组件的样式。Vue 3 中深度选择器的语法与 Vue 2 有所不同,以下是详细说明: 1. 深度选择器的作用 在 Vue 的单…...

基于Python的Diango旅游数据分析推荐系统设计与实现+毕业论文(15000字)
基于Python的Diango旅游数据分析推荐系系统设计与实现毕业论文指导搭建视频,带爬虫 配套论文1w5字 可定制到某个省份,加40 基于用户的协同过滤算法 有后台管理 2w多数据集 可配套指导搭建视频,加20 旅游数据分析推荐系统采用了Python语…...

Django ModelForm使用(初学)
1.目的是根据员工表字段,实现一个新增员工的数据填写页面 2.在views.py文件中按下面的格式写 定义 ModelForm 类:UserModelForm (自己命名的类名)使用时需要导入包 定义视图函数:user_model_form_add(在函…...

android ViewPager 管理 Fragment的预加载onCreate
一、前言 当ViewPager 加载多个 Fragment时候,怎么管理Fragment预加载。因为有些数据需要提前加载,第一个方便后面数据使用,提前初始化。或者预加载网络数据等。 二、实现示例 在onCreate方法进行数据预加载。如果在onCreateView函数里面&…...

运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
智慧园区场景视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。充分利用现有…...

国产编辑器EverEdit -告别东找西找!一键打开当前文件所在目录!
1 文件操作 2 应用场景 在文件编辑过程中,有时需要对文件进行一些操作,比如:在命令窗口输入文件路径、文件名,进入到文件目录,对文件进行压缩等,如果没有直达命令,用户需要通过文件管理器找到目…...

【分治法】线性时间选择问题
问题描述 给定线性序列中n个元素和一个整数k,1≤k≤n,要求在线性时间中找出这n个元素中第k小的元素 常规思路 常规思路是对序列先排序,落在第k个位置的元素就是第k小的元素。 这种方法的时间复杂度不是线性的,是O(nlogn)的时间…...

SpringBoot速成(16)项目部署P30
部署是一个非常重要的环节。部署的目的是将开发完成的程序运行在服务器上,让其他用户或系统能够访问和使用它。 让程序对外提供服务 开发环境的局限性:开发环境通常是本地计算机,仅供开发人员使用。但实际应用需要让其他用户(比如…...

【Mysql:数据库的基础操作】
目录 数据库创建,删除基础指令: 数据库的编码集: 数据库备份与恢复: 表的操作: 数据库创建,删除基础指令: show databases;//查看数据库列表//创建数据库 create database db_name; crea…...
Nacos Derby 远程命令执行漏洞修复建议
由于Nacos < 2.4.0 BETA 存在 Derby 远程命令执行漏洞,恶意攻击者利用此漏洞可以未授权执行SQL语句,最终导致任意代码执行。目前该漏洞PoC和技术细节已在互联网上公开。 一、漏洞情况分析 Nacos 是一个功能强大的服务注册与发现、配置管理平台&#…...
idea 2023.3.7常用插件
idea 2023.3.7常用插件 文档 idea 2019.3常用插件idea 2023.3.7常用插件 idea 2023.3.7常用插件 插件名称插件版本说明1AceJump3.5.9AceJump允许您快速将插入符号导航到编辑器中可见的任何位置。只需按“ctrl;”,键入一个字符,然后在Ace …...
DeepSeek和ChatGPT在科研课题设计和SCI论文写作中的应用
DeepSeek和ChatGPT在科研课题设计和SCI论文写作中的应用 一、DeepSeek和ChatGPT的基础理论 (理论讲解案例分析) 1.DeepSeek的技术架构 (1)DeepSeek的定义与核心目标 (2)DeepSeek的主要类型 如DeepSeek-R1、DeepSeek-V3等 (3)DeepSeek的主要创新点、优势能力以及主要应用场景 2.…...
kubeadm拉起的k8s集群证书过期的做法集群已奔溃也可以解决
kubeadm拉起的k8s集群证书过期的做法 这个是很久之前遇到的了,今天有空(心血来潮)就都回忆回忆写在这里为爱发光,部分内容来自arch先生(死党)的帮助。有时候有很多部门提了建k8s的需求,有些是临…...

2024年河北省职业院校技能大赛网络系统管理赛项样题解法
有问题请留言或主页私信咨询 2024年河北省职业院校技能大赛 网络系统管理赛项 网络构建 目录 任务描述 任务清单 (一)基础配置 (二)有线网络配置 (三)无线网络配置 (四&am…...

【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
本文项目编号 T 203 ,文末自助获取源码 \color{red}{T203,文末自助获取源码} T203,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…...

纯新手教程:用llama.cpp本地部署DeepSeek蒸馏模型
0. 前言 llama.cpp是一个基于纯C/C实现的高性能大语言模型推理引擎,专为优化本地及云端部署而设计。其核心目标在于通过底层硬件加速和量化技术,实现在多样化硬件平台上的高效推理,同时保持低资源占用与易用性。 最近DeepSeek太火了&#x…...
JDK 8+新特性(Stream API、Optional、模块化等)
JDK 8新特性(Stream API、Optional、模块化等) 一、Stream API 1.1 概述 Stream API 是 Java 8 引入的一个新的抽象概念,它允许以声明式的方式处理数据集合。Stream 不是一个数据结构,而是对数据源(如集合、数组等&…...
国产编辑器EverEdit - 独门暗器:自动监视剪贴板内容
1 监视剪贴板 1.1 应用场景 如果需要对剪贴板的所有历史进行记录,并进行分析和回顾,则可以使用监视剪贴板功能,不仅在EverEdit中的复制会记录,在其他应用的复制也会记录。 1.2 使用方法 新建一个空文档(重要:防止扰乱…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...