打破限制!自定义 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 使用方法 新建一个空文档(重要:防止扰乱…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
ES6从入门到精通:前言
ES6简介 ES6(ECMAScript 2015)是JavaScript语言的重大更新,引入了许多新特性,包括语法糖、新数据类型、模块化支持等,显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
