百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

聊聊 React 日常研发最常用的 15个 Hooks

zzlvtu 2024-09-04 23:02 6 浏览

家好,很高兴又见面了,我是"高级前端?进阶?",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

在 React Hooks (React < 16.8) 之前,开发人员需要编写类组件才能利用某些 React 功能。 但现在,React Hooks 提供了一种更符合人体工程学的方式来构建组件,因为可以使用有状态逻辑而无需更改组件层次结构。

1. useState

useState 是最重要也是最常用的 Hooks。

useState Hook 的目的是处理反应性数据,应用程序中发生变化的任何数据都称为状态,当数据发生变化时,React 会重新渲染 UI。

const [count, setCount] = React.useState(0);

2. useEffect

useEffect Hook 允许开发者在函数组件中执行副作用,包括:

  • 从 API 获取数据
  • 更新 DOM
  • 订阅事件等
// 组件挂载和state数据变化都会调用,很显然第二个参数相当于完整的state
React.useEffect(() => {
  alert('Hey, Nads here!');
});

// 组件首次挂载调用
useEffect(() => {
  fetch('https://api.example.com/data')
    .then((response) => response.json())
    .then((data) => setData(data));
}, []);

// 当state中的count变化的时候调用
React.useEffect(() => {
  fetch('nads').then(() => setLoaded(true));
}, [count]);

// 当组件被销毁或者组件从界面移除之前调用
React.useEffect(() => {
  alert('Hey, Nads here');
  return () => alert('Goodbye Component');
});

3. useContext

该 Hooks 允许开发者使用 React 的 Context API,它本身是一种机制,允许在组件树中共享数据而无需通过 props,从而消除 prop-drilling:

const ans = {
  right: '?',
  wrong: '?',
};

const AnsContext = createContext(ans);
// 创建一个context
function Exam(props) {
  return (
    // 任何内部组件都可以使用context的值
    <AnsContext.Provider value={ans.right}>
      <RightAns />
    </AnsContext.Provider>
  );
}

function RightAns() {
  //   从最近的父级Provider消费值
  const ans = React.useContext(AnsContext);
  return <p>{ans}</p>;
  //   以前需要包裹在AnsContext.Consumer中,现在已经不需要了
}

4. useRef

该 Hooks 允许创建一个可变对象(Mutable Object)。 当值经常变化时可以使用,就像 useState Hook 一样,但不同的是,当值变化时不会触发重新渲染。

其常见用例是从 DOM 中获取 HTML 元素。

function App() {
  const myBtn = React.useRef(null);
  const handleBtn = () => myBtn.current.click();
  return <button ref={myBtn} onChange={handleBtn}></button>;
}

5. useReducer

功能与 setState 非常相似,是使用 Redux 模式管理状态的不同方式。

useReducer 不直接更新状态,而是 dispatch 动作,将其发送到 reducer 函数,然后该函数计算出下一个状态。

function reducer(state, dispatch) {
  switch (action.type) {
    case 'increment':
      return state + 1;
    case 'decrement':
      return state - 1;
    default:
      throw new Error();
  }
}
function useReducer() {
  // state is the state we want to show in the UI.
  const [state, dispatch] = React.useReducer(reducer, 0);
  return (
    <>
      Count : {state}
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </>
  );
}

6. useMemo

useMemo Hook 允许开发者存储一个值,以便仅在其依赖项发生变化时才重新计算。这可以通过防止不必要的重新计算来帮助提高性能,推荐在需要进行昂贵计算时使用。

function useMemo() {
  const [count, setCount] = React.useState(60);
  const expensiveCount = useMemo(() => {
    return count ** 2;
  }, [count]);
  // 当count的值变化时候重新计算expensiveCount的值
}

7. useCallback

useCallback Hook 允许记忆一个函数,以便仅在其依赖项发生变化时才重新创建,从而可以通过防止不必要的重新渲染来帮助提高性能。

以下是如何使用 useCallback 来记忆函数的示例:

import React, { useState, useCallback } from 'react';

function SearchBar({ onSearch }) {
  const [query, setQuery] = useState('');
  // onSearch的prop变化时候更新
  const handleQueryChange = useCallback(
    (event) => {
      setQuery(event.target.value);
      onSearch(event.target.value);
    },
    [onSearch]
  );

  return <input type="text" value={query} onChange={handleQueryChange} />;
}

在此示例中,定义了一个带有 onSearch prop 函数的 SearchBar 组件。使用 useCallback Hooks 来记忆 handleQueryChange 函数,以便仅在 onSearch 函数更改时才重新创建它。

8. useImperativeHandle

useImperativeHandle Hook 允许开发者自定义在使用 ref 时向父组件公开的实例值。 当需要向父组件提供某个接口,但又不想公开子组件的所有内部实现细节时,这可能很有用。

以下是如何使用 useImperativeHandle 的示例:

import React, { useRef, useImperativeHandle } from 'react';

const Input = React.forwardRef((props, ref) => {
  const inputRef = useRef();
  // 允许开发者自定义在使用 ref 时向父组件公开的实例值
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    value: inputRef.current.value,
  }));

  return <input type="text" ref={inputRef} placeholder={props.placeholder} />;
});

function App() {
  const inputRef = useRef();
  const handleClick = () => {
    inputRef.current.focus();
  };
  return (
    <div>
      <Input ref={inputRef} placeholder="Type here" />
      {/*Input的ref传递下去*/}
      <button onClick={handleClick}>Focus input</button>
    </div>
  );
}

此示例定义了一个自定义 Input 组件,该组件在使用 ref 时使用 useImperativeHandle 向父组件公开焦点方法和 value 属性。 useImperativeHandle Hook 采用两个参数:ref 对象和回调函数,该函数返回一个对象,该对象具有应公开给父组件的属性和方法。

在 App 组件中使用 Input 组件并向其传递一个 ref 对象,还定义了一个 handleClick 函数,当单击按钮时,该函数调用 inputRef 对象的 focus 方法。

9. useLayoutEffect

工作原理与 useEffect Hook 相同,但有一点不同,回调将在渲染组件之后但在实际更新绘制到屏幕之前运行。 即,阻止视觉更新,直到回调完成。

function useLayoutEffectDemo() {
  const myBtn = React.useRef(null);

  React.useLayoutEffect(() => {
    const rect = myBtn.current.getBoundingClientRect();
    // scroll position before the dom is visually updated
    console.log(rect.height);
  });
}

10. useDebugValue

useDebugValue 是一个 Hook,允许开发者在 React DevTools 中显示自定义钩子的自定义调试信息。这对于调试 Hook 和了解幕后发生的事情很有用。

假设有 n 个使用相同逻辑的组件,那么可以单独定义自己的函数并且可以在其他组件中使用,但这里的关键是可以调试东西:

import { useState, useEffect, useDebugValue } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => setData(data));
  }, [url]);

  useDebugValue(data ? `Data loaded: ${data.length} items` : 'Loading...');

  return data;
}

在上面示例中定义了一个名为 useFetch 的自定义 Hook,用于从 URL 获取数据并返回。 使用 useDebugValue Hook 在 React DevTools 中显示自定义调试消息。 如果数据已加载,会显示一条消息,其中包含数据中的项目数。 如果数据仍在加载会显示一条消息“正在加载...”。

当在组件中使用 useFetch Hook 时,自定义调试消息将显示在 React DevTools 中。

11. useEffectEvent

上面说过,React 的 useEffect hook 用于在函数组件中执行副作用操作。而 useEffectEvent 是 React-use 库中提供的一个额外的 hook,是基于 useEffect 封装的一个事件监听器。

useEffectEvent 的主要场景是在函数组件中监听事件,这些事件包括:

  • Window 事件:resize、scroll、load、unload、beforeunload、popstate
  • Document 事件:mousemove、mousedown、mouseup、keypress、keydown、keyup、wheel
  • XMLHttpRequest 事件:loadstart、load、progress、abort、error、timeout、loadend
  • Custom 事件:自定义事件

useEffectEvent 与 useEffect 的区别在于,前者会自动移除事件监听器,而 useEffect 需要在返回函数中手动清除副作用。此外,useEffectEvent 还提供了一个回调函数,可以在事件发生时执行。

下面是一个 useEffectEvent 的使用示例:

import { useEffect, useState } from 'react';
import { useEffectEvent } from 'react-use';
function App() {
  const [scrollY, setScrollY] = useState(window.scrollY);
  const handleScrollY = () => {
    setScrollY(window.scrollY);
  };
  useEffect(() => {
    window.addEventListener('scroll', handleScrollY);
    // 需要手动移除
    return () => {
      window.removeEventListener('scroll', handleScrollY);
    };
  }, []);

  useEffectEvent('scroll', handleScrollY);
  return <div></div>;
}
export default App;

12. useInsertionEffect

调用 useInsertionEffect 在任何可能需要读取布局的效果触发之前插入样式:

// CSS-in-JS 库内部逻辑
let isInserted = new Set();
function useCSS(rule) {
  useInsertionEffect(() => {
    // 如前所述,不建议运行时注入 <style> 标签
    // 如果必须这么做,建议在 useInsertionEffect中。
    if (!isInserted.has(rule)) {
      isInserted.add(rule);
      document.head.appendChild(getStyleForRule(rule));
    }
  });
  return rule;
}

function Button() {
  const className = useCSS('...');
  return <div className={className} />;
}

方法接受两个参数:

  • setup: 具有副作用逻辑的函数。设置函数也可以选择返回一个清理函数,当组件从 DOM 中删除时,React 会自动运行。
  • 可选的 dependencies: setup 代码中引用的所有反应值的列表。

需要注意的是:

  • 副作用仅在客户端上运行,不会在服务器渲染期间运行
  • 无法从 useInsertionEffect 内部更新状态
  • useInsertionEffect 运行时无法获取 ref
  • useInsertionEffect 可能在 DOM 更新之前或之后运行,不应该依赖于在任何特定时间更新 DOM。
  • 与其他 Effect 不同的是,其他类型的 Effect 会为每个 Effect 触发清理,然后为每个 Effect 进行设置,useInsertionEffect 在一个组件上只会设置、清理一次。

13.useMemoCache

目前 useMemoCache(size: number): Array Hooks 仅用作实验性构建时自动记忆功能的编译目标(compilation target)。

这个 Hooks 手动调用是不安全的,虽然 API 强大但违反 React 规则,用户有责任确保使用安全, 而构建时工具保证转换后的代码安全地使用 API。

备注:useMemoCache 就是 React 内部为 React Forget 提供缓存支持的 hook。

React Forget :目标是确保 React 应用在默认情况下具有适量的响应性,即应用仅在状态值发生有意义的变化时才重新渲染。

从实现的角度来看,这意味着自动记忆,但 React 团队认为响应式框架是理解 React 和 Forget 的更好方式。React 目前会在对象标识更改时重新渲染。有了 Forget,React 会在语义值发生变化时才重新渲染—,从而不会产生深度比较的运行时成本。

14/15. useTransition/useDeferredValue

参考我写的另外一篇文章《React 为什么会推出 useTransition() 和 useDeferredValue() 两个 Hook?》

参考资料

https://dev.to/abhisheknaiidu/10-react-hooks-explained-3ino

https://medium.com/@AbidKazmi/all-react-hooks-in-one-short-4b0ed4b5a6e4

https://zhuanlan.zhihu.com/p/634140304

https://react.dev/learn/separating-events-from-effects

https://jser.dev/react/2023/03/18/useeffectevent/

https://qianduan.shop/blogs/detail/125#google_vignette

https://react.dev/reference/react/useInsertionEffect

https://github.com/facebook/react/pull/25123

https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023

https://www.protonshub.com/blogs/react-hooks-deep-dive-advanced-usage-and-patterns

相关推荐

什么是DPDK?DPDK的原理及学习学习路线总结

一、什么是DPDK  对于用户来说,它可能是一个性能出色的包数据处理加速软件库;对于开发者来说,它可能是一个实践包处理新想法的创新工场;对于性能调优者来说,它可能又是一个绝佳的成果分享平台。 ...

每天进步一点:两分钟解决kvm下windows虚拟机鼠标不跟随

跟随昨天文章做测试的朋友们应该和我一样遇到了vnc连接windows鼠标不跟随的问题,经过一番查找有两种解决办法:1.编辑配置文件命令virshedittest或者直接vi/etc/libvir...

PC虚拟化主流:KVM、XEN、OpenVZ详解

目前,PC的虚拟化逐渐成为互联网发展的大趋势,我们知道,KVM、XEN、OpenVZ是虚拟化的三种方式,今天我们就来探讨这三种虚拟化的优势和劣势。1、pc虚拟化——KVMKVM是完整的硬件虚拟化,可在...

Windows上使用QEMU创建aarch64(ARM64)虚拟机

前言随着国产化的推进,现在采用ARM、MIPS的机器越来越多,作为开发、运维人员要调测软件总不能每种架构的机器都去买一台吧?主要像博主这样的穷B,实在也是承受不起。。需要的工具...

高度致敬Windows!开源优麒麟20.04 LTS发布:支持5年

优麒麟团队宣布,优麒麟(UbuntuKylin)开源操作系统20.04LTS正式版已经发布,代号FocalFossa,全球同步发布的还有Ubuntu20.04、Lubuntu20.04、Xub...

极空间虚拟机上线了,一学就会!小白保姆级使用教程

友情提示本文涉及内容较多,篇幅在4500字左右,为了对小白用户更加友好,图片示例多达60张。整个文章部分为三个阶段,准备-初探-实战。其中实战部分包含Windows系统,ikuai软路由系统,iSto...

Windows Subsystem for Linux现以应用形式上架Microsoft Store

微软今天宣布WindowsSubsystemforLinux(WSL)作为一款应用上架Windows11端的MicrosoftStore。也就是说,现在WSL以应用的方式通过...

Windows Server 2019 Core 虚拟机系统镜像制作

WindowsServer2019Core简介WindowsServer2019是微软于2018年11月13日发布的新一代WindowsServer服务器操作系统,基于Win10180...

微软商店中的WSL预览版现已可用!Windows 11用户狂喜

...

在NAS上安装Win10,24小时待命的云电脑达成√

#头条创作挑战赛#引子...

免费开源虚拟机VirtualBox 7.0.12发布:修复TPM和黑屏问题

IT之家10月18日消息,甲骨文近日发布了VirtualBox7.0.12维护版本更新,重点修复此前版本中用户反馈和官方发现的BUG,改善了对LinuxKernel6.4/6.5...

KVM Cloud 虚拟机管理系统安装部署

KVMCloud介绍KVMCloud是一款基于KVM实现的适用于小微企业的虚拟机管理系统,支持如下功能:基于KVM的VM基础功能(创建、启动、停止、重装、webVNC等功能)使用NFS作为磁盘...

个人KVM 虚拟化学习笔记(kvm虚拟化管理平台)

一、KVM原理二、KVM基础功能2.1CPU2.2内存2.3存储2.4网络三、KVM高级功能...

kvm虚拟化之ESXi到KVM之v2v迁移(esxi虚拟机迁移到另一个esxi)

1.ESXi到KVM之v2v情况说明(1).配置任务列表:1)VMwareESXi虚拟平台下linux系统迁移到KVM虚拟平台。2)VMwareESXi虚拟平台下windows系统迁移到KVM虚拟平台...

unraid下虚拟机安装Windows(vmware安装unraid)

unraid下虚拟机安装Windows使用unraid也有一段时间了,主要是做数据备份,以及docker容器的安装测试,今天有空测试一下VMS虚拟机的使用,用在unraid上安装windows7操作系...