自定义 React Hooks:编写高效、整洁和可重用代码的秘密武器

欢迎来到神奇的 React 世界

大家好!在 React 的世界中,有一个强大的秘密武器,它往往隐藏在显而易见的地方,由于缺乏理解或熟悉而没有得到充分利用。

这个强大的工具,被称为自定义 React hooks,可以彻底改变我们编写 React 应用程序代码的方式。通过提取组件中的有状态逻辑,自定义 hooks 允许我们大大提高代码的效率、可读性和重用性。

然而,自定义 hooks 不仅仅是一个开发人员工具箱中的功能或工具。它们代表了我们开发范式的转变,一个引领我们重新思考在 React 应用程序中传统管理状态和副作用方式的新思维模式。

开箱即用:什么是 Hooks?

在本文中,我们将消除自定义 hooks 的概念的迷雾,深入探究它们的结构,并探索创建我们自己的过程。我们将采取实际的方法,举例来演示它们在真实场景中的用法。

我们的目标不仅是理解自定义 hooks 是什么或它们如何工作。这是全面接受其背后的哲学并赏识它们为我们的代码带来的优雅、简单和强大。

为什么自定义 Hooks 很重要

关键在于:hooks 很酷,但是自定义 hooks?它们就是可以使你的 React 代码水平更进一步的秘密配方。就像一个功能强大的工具,可以完全定制你的特定需求。一个为你量身定做的解决方案。

准备潜入自定义 React Hooks 的世界吧!

为什么我们需要自定义 Hooks

你可能会想:"我们已经有 hooks 了,为什么还需要自定义 hooks?"这是一个非常好的问题,答案很简单。自定义 hooks 是关于使你的代码更可读、更易管理和最重要的可重用。

提高代码可读性

在软件开发中,可读性非常重要。编写代码不仅要让它工作,还要让它易于理解。使用自定义 hooks,您可以将复杂的逻辑封装到单独的函数中。这样,当其他人(甚至将来的你)查看代码时,他们可以轻松理解发生了什么。

流线型代码管理

在处理大型应用程序时,管理代码可能是一个具有挑战性的任务。通过使用自定义 hooks,您可以将代码拆分为更小、更易管理的部分。就像整洁地将代码组织到不同的盒子中,每个盒子都服务于特定的目的。

促进代码重用

编程的基本原则之一是 DRY,即 Don’t Repeat Yourself。自定义 hooks 允许您编写一段逻辑一次,并在多个组件中重用它。这意味着更少的重复,更少的错误机会和更高的效率。

简而言之,自定义 hooks 是你的 React 工具箱中的宝贵工具。它们可以帮助您编写更干净,更易维护的代码,使您作为开发人员的生活更轻松,更愉快。

💡 注意: 使用一个开源的工具链比如 Bit 来发布、版本化和在所有的项目中重用你的自定义 hooks ,使用一个简单的 bit import your.username/yourCustomHook 命令,减少代码重复并最小化模板。

理解自定义 Hook

既然我们已经知道为什么需要自定义 hooks,那么让我们深入研究它们的实际样子和功能。 这一点很重要,因为理解自定义 hook 的结构将帮助您在未来编写自己的自定义 hook。

基本外观

自定义 hook 只是一个 JavaScript 函数。唯一的规则是它的名称应该以 “use” 开头。这是一个约定,可以帮助你和其他人识别这个函数是一个 hook。例如,一个自定义钩子可能被命名为 useFetchData

自定义 Hook 内部是什么

在自定义 hook 内部,你可以使用其他 hooks,比如 useStateuseEffect 等。例如,useFetchData 钩子可以使用 useStateuseEffect 钩子来获取数据并在状态中维护它。

返回值

自定义钩子可以返回任何你想要它返回的内容。这可以是一个单一的值、一个数组或者甚至是一个对象。你返回什么取决于你的组件需要什么。在我们的 useFetchData 示例中,自定义钩子可能会返回获取的数据以及加载状态和错误状态。

自定义 Hook 示例

下面是一个简单的例子来说明自定义 hook, 这里有一个自定义钩子的示例来说明其结构:

function useFetchData(url) {const [data, setData] = useState(null); const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {fetch(url).then(response => response.json()).then(data => {setData(data);setLoading(false);  }).catch(error => {setError(error);setLoading(false);});}, [url]);return { data, loading, error };  
}

在这个例子中,useFetchData是一个自定义钩子,它从提供的URL获取数据。它使用useState来管理数据、加载状态和错误状态,并使用useEffect来处理数据获取。然后这个钩子返回一个包含数据、加载状态和错误状态的对象。

所以,这就是自定义钩子的结构。一开始它们可能看起来有点复杂,但是一旦你理解了它们的结构,它们可以极大地简化你的 React 代码并使其更易于管理。

创建自己的自定义 Hook 的步骤

Jefferson Santos

既然我们已经解剖了自定义钩子的结构,是时候将这些知识付诸行动,自己创建一个了。别担心,这听起来没有那么可怕。事实上,有了这些简单的步骤,你很快就可以创建自己的自定义钩子了!

第一步:定义你的函数

首先,我们需要定义一个 JavaScript 函数。还记得我们之前讨论过的命名约定吗?函数名称应以 “use” 开头。例如,让我们创建一个处理表单输入的钩子。我们将其命名为 useFormInput

function useFormInput() {// 逻辑将在这里 
}

第二步:用 Hooks 添加逻辑

接下来,我们将使用 React 中的内置钩子向自定义钩子添加逻辑。 在我们的 useFormInput 钩子中,我们将使用 useState 钩子来管理输入值。

function useFormInput() {const [value, setValue] = useState("");// 更多逻辑将在这里  
}

第三步:处理改变

现在,我们需要处理对输入值的更改。 我们将创建一个在输入更改时更新值的函数。

function useFormInput() {const [value, setValue] = useState("");function handleChange(event) {setValue(event.target.value);}// 更多逻辑将在这里
}

第四步:返回需要的

最后,我们需要返回组件需要的任何内容。在这种情况下,我们的组件将需要该值和 handleChange 函数。 所以,我们将返回一个包括两者的对象。

function useFormInput() {const [value, setValue] = useState("");function handleChange(event) {setValue(event.target.value);}return { value, handleChange };
}

就是这样!你刚刚创建了自己的自定义钩子。你现在可以在函数组件中使用这个钩子来处理表单输入。它是可重用的、干净的,并且使你的组件更具可读性。 最棒的是?你可以为任何你想要的创建一个自定义钩子,使你的代码更高效,更易于管理。

10 个实用示例:创建和使用自定义 Hooks

Christina Morillo

既然你已经适应了创建自定义钩子,那么让我们更进一步。 我们将探索十个可立即在项目中使用的自定义钩子的实用示例。 让我们深入研究!

useLocalStorage

useLocalStorage是一个自定义的钩子,它简化了在 React 应用中使用本地存储的方法。它允许您从本地存储读取、写入和删除数据。

function useLocalStorage(key, initialValue) {const [storedValue, setStoredValue] = useState(() => {try {const item = window.localStorage.getItem(key);return item ? JSON.parse(item) : initialValue;} catch (error) {console.log(error);return initialValue;}});const setValue = value => {try {const valueToStore = value instanceof Function ? value(storedValue) : value;setStoredValue(valueToStore);window.localStorage.setItem(key, JSON.stringify(valueToStore));} catch (error) {console.log(error);}};return [storedValue, setValue];
}

useDocumentTitle

useDocumentTitle是一个自定义钩子,可以轻松地从 React 组件更改文档标题。

function useDocumentTitle(title) {useEffect(() => {document.title = title;}, [title]);
}

useEventListener

useEventListener是一个自定义钩子,可以轻松地向任何 DOM 元素添加事件侦听器。

function useEventListener(eventName, handler, element = window) {useEffect(() => {const isSupported = element && element.addEventListener;if (!isSupported) return;element.addEventListener(eventName, handler);return () => {element.removeEventListener(eventName, handler);};}, [eventName, element, handler]);
}

useOnClickOutside

useOnClickOutside是一个自定义钩子,当用户点击特定组件之外时触发事件。这对于关闭下拉菜单等很方便。

function useOnClickOutside(ref, handler) {useEffect(() => {const listener = event => {if (!ref.current || ref.current.contains(event.target)) return;handler(event);};document.addEventListener("mousedown", listener);document.addEventListener("touchstart", listener);return () => {document.removeEventListener("mousedown", listener);document.removeEventListener("touchstart", listener);};}, [ref, handler]);
}  

useHover

useHover是一个自定义钩子,用于检测鼠标是否悬停在元素上。

function useHover() {const [value, setValue] = useState(false);const ref = useRef(null);const handleMouseOver = () => setValue(true);const handleMouseOut = () => setValue(false);useEffect(() => {const node = ref.current;if (node) {node.addEventListener('mouseover', handleMouseOver);node.addEventListener('mouseout', handleMouseOut);return () => {node.removeEventListener('mouseover', handleMouseOver);node.removeEventListener('mouseout', handleMouseOut);};}},[ref] // Recall only if ref changes);return [ref, value];
}

useOnlineStatus

useOnlineStatus是一个自定义钩子,用于检测用户当前是否在线或离线。

function useOnlineStatus() {const [isOnline, setOnline] = useState(navigator.onLine);const goOnline = () => setOnline(true);const goOffline = () => setOnline(false);useEffect(() => {window.addEventListener('online', goOnline);window.addEventListener('offline', goOffline);return () => {window.removeEventListener('online', goOnline);window.removeEventListener('offline', goOffline);};}, []);return isOnline;
}

useWindowSize

useWindowSize是一个自定义钩子,允许你访问当前窗口大小。

function useWindowSize() {const [size, setSize] = useState([window.innerWidth, window.innerHeight]);useLayoutEffect(() => {const updateSize = () => {setSize([window.innerWidth, window.innerHeight]);};window.addEventListener('resize', updateSize);updateSize();return () => window.removeEventListener('resize', updateSize);}, []);return size;
}

useMediaQuery

useMediaQuery是一个自定义钩子,允许你在组件中使用媒体查询。

function useMediaQuery(query) {const [matches, setMatches] = useState(window.matchMedia(query).matches);useEffect(() => {const mediaQueryList = window.matchMedia(query);const documentChangeHandler = () => setMatches(mediaQueryList.matches);mediaQueryList.addListener(documentChangeHandler);documentChangeHandler();return () => {mediaQueryList.removeListener(documentChangeHandler);};}, [query]);return matches;
}

useDebounce

useDebounce是一个自定义钩子,有助于延迟函数调用和消除给定的值。

function useDebounce(value, delay) {const [debouncedValue, setDebouncedValue] = useState(value);useEffect(() => {const handler = setTimeout(() => {setDebouncedValue(value);}, delay);return () => {clearTimeout(handler);};}, [value, delay]);return debouncedValue;
}

useInterval

useInterval是一个自定义钩子,允许你使用指定的间隔设置递归函数调用。这在需要重复执行函数时很有用,比如更新计时器或轮询服务器。

function useInterval(callback, delay) {const savedCallback = useRef();// Remember the latest callback.useEffect(() => {savedCallback.current = callback;}, [callback]);// Set up the interval.useEffect(() => {function tick() {savedCallback.current();}if (delay !== null) {let id = setInterval(tick, delay);return () => clearInterval(id);}}, [delay]);
}

我将暂停到这里,但我相信这些示例应该足以让你了解自定义钩子的可能性。

你可以为你需要的任何创建一个自定义钩子。

所以继续吧,开始创建和使用自定义钩子,使你的代码比以往任何时候都更干净、可重用!

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

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

相关文章

查找局域网树莓派raspberry的mac地址和ip

依赖python库: pip install socket pip install scapy运行代码: import socket from scapy.layers.l2 import ARP, Ether, srpdef get_hostname(ip_address):try:return socket.gethostbyaddr(ip_address)[0]except socket.herror:# 未能解析主机名ret…

Electron Apple SignIn 登录

本人写博客,向来主张:代码要完整,代码可运行,文中不留下任何疑惑。 最讨厌写博客,代码只留下片段,文中关键的东西没写清楚。之前看了那么多文章,就是不告诉我clientId从哪来的。 官方资料地址&…

WordPress后台底部版权信息“感谢使用 WordPress 进行创作”和版本号怎么修改或删除?

不知道各位WordPress站长在后台操作时,是否有注意到每一个页面底部左侧都有一个“感谢使用 WordPress 进行创作。”,其中WordPress还是带有nofollow标签的链接;而页面底部右侧都有一个WordPress版本号,如下图中的“6.4.2 版本”。…

chisel入门初步1——基4的booth编码的单周期有符号乘法器实现

基4的booth编码乘法器原理说明 基2的booth编码 本质来说就是一个裂项重组,乘法器最重要的设计是改变部分积的数量,另外在考虑有符号数的情况下,最高位符号位有特别的意义。 (注:部分积是指需要最后一起加和的所有部分…

使用的uview 微信高版本 头像昵称填写能力

<template><view><button class"cu-btn block bg-blue margin-tb-sm lg" tap"wxGetUserInfo">一键登录</button><view><!-- 提示窗示例 --><u-popup :show"show" background-color"#fff">&…

关于C#中的async/await的理解

1. 使用async标记的方法被认为是一个异步方法&#xff0c;如果不使用await关键字&#xff0c;调用跟普通方法没有区别 static async Task Main(string[] args){Console.WriteLine("主线程id&#xff1a;" Thread.CurrentThread.ManagedThreadId);TestAwait();Consol…

翻译: Streamlit从入门到精通六 实战缓存Cache请求数据

Streamlit从入门到精通 系列&#xff1a; 翻译: Streamlit从入门到精通 基础控件 一翻译: Streamlit从入门到精通 显示图表Graphs 地图Map 主题Themes 二翻译: Streamlit从入门到精通 构建一个机器学习应用程序 三翻译: Streamlit从入门到精通 部署一个机器学习应用程序 四翻译…

从matlab的fig图像文件中提取数据

这里用的是openfig&#xff08;&#xff09;函数打开的fig文件 →→→【matlab 中 fig 数据提取】 很简洁 →→→【MATLAB提取 .fig 文件中的数据】 这个给出了包含多个曲线的情况 →→→【提取matlab fig文件里的数据和legend】 chatgpt给出的方法 打开fig文件并保存数据 我的…

StarRocks 生成列:百倍提速半结构化数据分析

半结构化分析主要是指对 MAP&#xff0c;STRUCT&#xff0c;JSON&#xff0c;ARRAY 等复杂数据类型的查询分析。这些数据类型表达能力强&#xff0c;因此被广泛应用到 OLAP 分析的各种场景中&#xff0c;但由于其实现的复杂性&#xff0c;对这些复杂类型分析将会比一般简单类型…

【单片机】改写DS2431芯片的地址码,地址ROM,DS2431芯片解密

对DS2431里面的128字节可以进行任意读写&#xff0c;方式可以看这里&#xff1a;https://blog.csdn.net/x1131230123/article/details/132248958 但DS2431芯片的地址码是光刻不可修改的&#xff0c;所以只有使用模拟芯片。 原理&#xff1a; https://www.dianyuan.com/article…

K8S Informer机制原理解读 | 架构设计

在Kubernetes系统中&#xff0c;组件之间通过HTTP协议进行通信&#xff0c;在不依赖任何中间件的情况下需要保证消息的实时性、可靠性、顺序性等。那么Kubernetes是如何做到的呢&#xff1f;答案就是Informer机制。Kubernetes的其他组件都是通过client-go的Informer机制与Kuber…

Java线程池配置由繁至简,找到适合自己的天命线程池

Java线程池配置由繁至简&#xff0c;找到适合自己的天命线程池 任务队列workQueue和饱和策略handler什么时候登场&#xff1f; 首先这里有几道经常考的线程池面试题&#xff1a; 简单介绍下线程池&#xff0c;核心数从corePoolSize 到maximumPoolSize 的变化过程&#xff1f;…

我用 ChatGPT 做了一次探索性数据分析,真的太太太实用了!

ChatGPT 经过短短1年时间的发展&#xff0c;其功能越来越强&#xff0c;现在已经是大多数企业和个人不可或缺的助手。特别是最新的 GPT-4 版本&#xff0c;专门在左边菜单栏给出了两个工具&#xff08;一个是数据分析&#xff0c;另一个是根据文字描述生成图片&#xff09;&…

教育的本质与教师发展:对能力大赛模板化现象的深度反思与批判——以快速技术迭代背景下的教学策略为审视视角

在我国当前的教育体系中&#xff0c;教师能力大赛等活动在一定程度上确实扮演了提升教师专业素养、推动教学改革的角色。它们通过竞争机制激发了教师自我提升的动力&#xff0c;并提供了一个展示教师教学才华的平台。然而&#xff0c;随着时间推移&#xff0c;此类活动却呈现出…

Opencv小项目——手势数字刷TIKTOK

​ 写在前面&#xff1a; 很久没更新了&#xff0c;之前的实习的记录也算是烂尾了&#xff0c;但是好在自己的实习记录还是有的&#xff0c;最近也忙碌了很多&#xff0c;终于放假了&#xff0c;今天下午正好没事&#xff0c;闲来无事就随便做个小玩意吧。 思来想去&#xff…

yolo9000:Better, Faster, Stronger的目标检测网络

目录 一、回顾yolov1二、yolov2详细讲解2.1 Better部分创新点&#xff08;1&#xff09;Batch Normalization(批量归一化)&#xff08;2&#xff09;High Resolution Classifier---高分辨率分类器&#xff08;3&#xff09;Anchor Boxes---锚框&#xff08;4&#xff09;Dimens…

k8s学习-Deployment

Kubernetes通过各种Controller来管理Pod的生命周期 。 为了满足不同业 务 景 &#xff0c; Kubernetes 开发了Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job等多种Controller。我们⾸先学习最常用Deployment。 1.1 Kubectl命令直接创建 第一种是通过kubectl命令直接…

ROS第 9 课 编写简单的服务端 Server

文章目录 第 9 课 编写简单的服务端 Server1.创建服务器代码2.运行服务器节点 第 9 课 编写简单的服务端 Server 1.创建服务器代码 注意&#xff1a;在创建服务器代码之前&#xff0c;需要先创建工作空间和功能包&#xff0c;具体操作过程可前往目录“第4课 创建工作空间与功能…

蓝桥杯每日一题---基数排序

题目 分析 在实际的比赛过程中很少会自己手写排序&#xff0c;顶多是定义一下排序规则。之所以要练习一下基数排序&#xff0c;是因为在后续学习过程中学到后缀数组时需要自己手写基数排序&#xff0c;那么这里使用的方法也和后缀数组一致&#xff0c;理解这里也便于后缀数组的…

领域特定语言(Domain-Specific Language, DSL)在 Visual Studio 2022中的实验——建立领域模型

一、环境 dotnet --version 8.0.101 Microsoft Visual Studio Enterprise 2022 (64 位) - Current 版本 17.8.4 已安装组件 ComponentLinkVisual Studiohttp://go.microsoft.com/fwlink/?LinkId185579Visual Studio SDKhttps://go.microsoft.com/fwlink/?li…