react之自定义hooks

任何相对独立、复用性强的逻辑,都可以 extract 为自定义 Hook,自定义 Hook 是一种复用 React 的状态逻辑的函数。
自定义 Hook 的主要特点是:

  • 抽象组件间的状态逻辑,方便复用
  • 让功能组件更纯粹,更易于维护
  • 自定义 Hook 可以调用其他 Hook

为什么要用自定义 Hook?

  1. 提炼能复用的逻辑
    许多组件有相似的状态逻辑,使用自定义 Hook 可以很方便地提取出来复用。
  2. 解决复杂组件的可读性问题
    使用自定义 Hook 将复杂组件拆分为更小的功能独立的函数,有助于提高代码的可读性。
  3. 管理数据更新
    使用独立的 Hook 函数来管理数据请求、处理异步逻辑、数据缓存等,易于维护。
  4. 分离状态逻辑
    自定义 Hook 让函数组件更纯粹,只负责 UI,状态逻辑则交给 Hook。
  5. 调用其他 Hook
    自定义 Hook 本身还可以调用 useState、useEffect 等其他 React Hook。

以下是我总结的一些常用的hooks

1、useUpdateEffect

useUpdateEffect作用

useUpdateEffect 是一个自定义的 React Hook,用于在组件更新时执行副作用操作。它类似于 React 的 useEffect,但是会忽略组件的初始渲染阶段,只在组件更新时执行副作用操作。

在 React 中,useEffect 会在组件的每次渲染(包括初始渲染)完成后执行副作用操作。但有时候我们只想在组件更新时执行某些操作,而不关心初始渲染阶段的操作。这就是 useUpdateEffect 的用途。

以下是一个示例:

import { useEffect, useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('useEffect - Component has rendered');
  });

  useUpdateEffect(() => {
    console.log('useUpdateEffect - Component has updated');
  });

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

在上述示例中,当点击 “Increment” 按钮时,count 的值会增加并触发组件的重新渲染。useEffect 会在每次渲染后执行,而 useUpdateEffect 只会在组件更新时执行。

通过使用 useUpdateEffect,你可以在组件更新时执行一些特定的副作用操作,如请求数据、更新状态等,而不需要关心初始渲染阶段的操作。

为什么会需要用到useUpdateEffect

在某些情况下,我们希望在 React 组件更新时执行一些特定的副作用操作,而不在初始渲染阶段执行这些操作。这种情况下,我们可以使用类似于 useUpdateEffect 的自定义 Hook。

以下是一些使用 useUpdateEffect 的常见情况:

  1. 避免初始渲染时执行副作用:有些副作用操作可能只需要在组件更新时执行,例如发送网络请求、更新特定状态等。使用 useUpdateEffect 可以确保这些副作用操作在初始渲染时被跳过,只在组件更新时执行。

  2. 监听特定状态的变化:有时我们只关心特定状态的变化,并希望在状态发生变化时执行相应的操作。通过将状态值作为 useUpdateEffect 的依赖项,可以确保副作用操作只在这些状态发生变化时触发。

  3. 更新外部资源或库:有些第三方库或外部资源可能需要在组件更新时进行更新或重新初始化。使用 useUpdateEffect 可以确保在组件更新时调用相应的函数或方法,以便正确地更新这些外部资源。

通过使用 useUpdateEffect,我们可以更加精确地控制副作用操作的触发时机,避免不必要的重复执行,以及在需要时处理特定的更新逻辑。

需要注意的是,React 自带的 useEffect 可以处理大多数情况下的副作用操作,而 useUpdateEffect 是在某些特定场景下的补充工具。在大多数情况下,使用 useEffect 即可满足需求。

自定义useUpdateEffect

要自定义一个类似于 useUpdateEffect 的自定义 Hook,你可以借助 React 的 useEffectuseRef Hooks 来实现。以下是一个示例代码:

import { useEffect, useRef } from 'react';

function useUpdateEffect(effect, dependencies) {
  const isMounted = useRef(false);

  useEffect(() => {
    if (isMounted.current) {
      effect();
    } else {
      isMounted.current = true;
    }
  }, dependencies);
}

// 使用示例
function MyComponent() {
  const [count, setCount] = useState(0);

  useUpdateEffect(() => {
    console.log('Component has updated');
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

在上述示例中,我们创建了一个名为 useUpdateEffect 的自定义 Hook。它接受两个参数:effectdependencies。在内部,我们使用了 useRef 来创建一个标记是否已经完成初始渲染的变量 isMounted

useEffect 中,我们检查 isMounted 的值。如果 isMounted 的值为 true,则表示组件已经完成了初始渲染,此时执行传入的 effect 函数。否则,将 isMounted 的值设置为 true,表示组件已完成初始渲染。

在使用时,你可以像使用 useEffect 一样,传入 effect 函数和依赖项数组 dependencies,并且 effect 函数只会在组件更新时执行。

2、useTitle

useTitle 是一个相对经典的自定义 React Hook ,用来控制浏览器标题:

定义useTitle

javascript">import { useState, useEffect } from 'react';

function useTitle(initialTitle) {
  const [title, setTitle] = useState(initialTitle);
 
  useEffect(() => {
    document.title = title;
  }, [title]);

  return setTitle;
}

使用useTitle:

javascript">function Page() {
  const setTitle = useTitle('Default Title');
 
  return (
    <Button onClick={() => setTitle('New Title')}>
      Click me
    </Button>
  )
}

点击按钮后,浏览器标题会变成"New Title"。
它的工作原理是:

  • 保存标题的 state ,并记录修改 setTitle()
  • 用 useEffect 监测 title 变化,设置 document.title
    所以一旦我们调用 setTitle(‘New Title’) 改变 state ,useEffect 就会执行,设置新的浏览器标题。
    useTitle 的优点是:
  • 抽象出设置标题的逻辑,任何组件都可以共享
  • 让组件更纯粹,只需要调用 setTitle() 接口即可
    我们甚至可以抽象为更通用的 Hook:
javascript">js
function useDocumentTitle(title) {
  useEffect(() => {
    document.title = title;
  }, [title]);
}

function Page() {
  useDocumentTitle('Default Title');
  // ...
}

通过自定义 Hook ,可以方便地在任何组件控制标题。

3、useForceUpdate

定义useForceUpdate

javascript">import { useState } from 'react';

function useForceUpdate() {
  const [value, setValue] = useState(0); 
  
  return () => {
    setValue(value => value + 1); 
  };
}

useForceUpdate的使用

javascript">const forceUpdate = useForceUpdate();

// 模拟更新组件
forceUpdate();

这个 Hook 返回了一个更新函数。在调用这个函数时,使用useState强制组件重新渲染。
这是基于以下原理实现的:

  • useState()会触发组件重新渲染
  • state变化后,组件函数会重新执行
    函数式组件只有 state 或 props 变化时才会更新。
    使用此 Hook 我们可以主动触发组件更新。
    比如在使用过时数据时:
javascript">// 过时数据 
const { data } = useSomeHook();

// 更新组件
const forceUpdate = useForceUpdate();
setInterval(() => {
  forceUpdate();
}, 5000);

每5秒强制组件一次,保证拿到最新数据。

4、useDebounce

定义

javascript">const useDebounce = (value, delay) => {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);   
    }, delay);
    return () => {
      clearTimeout(handler); 
    };
  }, []); // 设为空数组[]

  useEffect(() => {
    clearTimeout(handler);     
    handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
  }, [value]);  // 只依赖 value

  return debouncedValue;
};

使用

javascript">const inputValue = useDebounced(searchTerm, 500);

这里 每当searchTerm变化时,会设置一个 500ms 的定时器。只有500ms内没有再改变searchTerm,才会更新debouncedValue
这实现了防抖功能:在一定时间内停止触发, 只执行最后的动作。

5、useThrottle

定义

javascript">const useThrottle = (value, limit) => {
  const [throttledValue, setThrottledValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setThrottledValue(value);
    }, limit);
    return () => {
      clearTimeout(handler);
    };
  }, []); // 应设为空数组[]  
  
  useEffect(() => {
    clearTimeout(handler);
    handler = setTimeout(() => {
      setThrottledValue(value);    
    }, limit);
  }, [value, limit]); 
  
  return throttledValue;  
};

使用

javascript">const throttledValue = useThrottle(inputValue, 1000);

这里 每次inputValue变化时,会开始一个计时器。1s后才会更新throttledValue,实现了节流功能。

6、useInterval

定义

javascript">const useInterval = (callback, delay) => {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }); 

  useEffect(() => {
    function tick() {
      savedCallback.current();          
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);        
      return () => clearInterval(id);
    }
  }, [delay]);
}

使用

javascript">useInterval(() => {
  // ...
}, 1000); 

这里每1000ms就会调用一次回调函数,实现了定时执行指定函数的功能。
有任何问题欢迎留言讨论学习


http://www.niftyadmin.cn/n/1007795.html

相关文章

C/C++基础讲解(一百三十)之经典篇(出现次数/送到磁盘/字母大小写转换)

C/C++基础讲解(一百三十)之经典篇(出现次数/送到磁盘/字母大小写转换) 程序之美 前言 很多时候,特别是刚步入大学的学子们,对于刚刚开展的计算机课程基本上是一团迷雾,想要弄明白其中的奥秘,真的要花费一些功夫,我和大家一样都是这么啃过来的,从不知到知知,懵懂到入门…

【备战秋招】每日一题:2023.05.24-华为OD机试(第一题)-连续内存合并

为了更好的阅读体检&#xff0c;可以查看我的算法学习博客 在线评测链接:P1300 题目描述 塔子哥最近对在开发一个简单的操作系统&#xff0c;今天他的任务是为操作系统的动态内存管理模块实现内存块的合并功能。 介绍一下操作系统的内存管理模块:操作系统的动态内存管理模块…

SpringBoot3【② Web开发】

SpringBoot3-Web开发 SpringBoot的Web开发能力&#xff0c;由SpringMVC提供。 0. WebMvcAutoConfiguration原理 1. 生效条件 AutoConfiguration(after { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.clas…

Spring Boot 中的 Redis 的数据操作配置和使用

Spring Boot 中的 Redis 的数据操作配置和使用 Redis 是一种高性能的 NoSQL 数据库&#xff0c;它支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合和有序集合。Redis 还提供了丰富的命令&#xff0c;可以对数据进行快速的 CRUD 操作。Spring Boot 是一个基于 Sprin…

chatgpt赋能python:在Python中运行程序的方法介绍

在Python中运行程序的方法介绍 Python是一种广泛使用的编程语言&#xff0c;也是人工智能和数据科学领域的首选。在这篇SEO文章中&#xff0c;我们将介绍Python中运行程序的几种方法。 1. 在Python环境中运行程序 Python环境是一个Python解释器及其标准库的集合。为了在Pyth…

开源 sysgrok — 用于分析、理解和优化系统的人工智能助手

作者&#xff1a;Sean Heelan 在这篇文章中&#xff0c;我将介绍 sysgrok&#xff0c;这是一个研究原型&#xff0c;我们正在研究大型语言模型 (LLM)&#xff08;例如 OpenAI 的 GPT 模型&#xff09;如何应用于性能优化、根本原因分析和系统工程领域的问题。 你可以在 GitHub …

2023年6月Web3行业月度发展报告区块链篇 | 陀螺科技会员专享

6月&#xff0c;合规与监管成为本月加密领域的主旋律&#xff0c;在海外&#xff0c;SEC接连起诉币安与Coinbase两大交易平台&#xff0c;并将除BTC、ETH、USD系等的几乎所有加密货币列为证券&#xff0c;引发市场哗然&#xff0c;行情也与之紧密关联&#xff0c;随着做市商缓慢…

Linux 抓包工具——tcpdump

1 简介 用简单的话来定义tcpdump&#xff0c;就是&#xff1a;dump the traffic on a network&#xff0c;根据使用者的定义对网络上的数据包进行截获的包分析工具。 tcpdump可以将网络中传送的数据包的“头”完全截获下来提供分析。它支持针对网络层、协议、主机、网络或端口…