首页 > 其他分享 >通过8个常用hook手把手教你封装hooks

通过8个常用hook手把手教你封装hooks

时间:2023-03-01 11:15:27浏览次数:54  
标签:function const 手把手 value hook hooks return useEffect

通过8个常用hook手把手教你封装hooks

FE情报局 FE情报局    

对于使用 react 的同学来说,hook 一定不陌生,但是如何封装 hook 以及在业务中怎么使用封装的 hook,很多同学并没有一个很好的实践,这篇文章就通过10个常用的 hook 让大家学会封装 hook,能够在自己的业务中使用,提高复用率,减少开发成本

前沿

hook 到底有什么作用呢?它可以让你对一些功能组件重复逻辑进行封装,分离组件并将其功能细化,让组件逻辑变的简单明了,逻辑共享变的更容易,减少了代码的重复性,维护和更新变的更简单易懂

hook 的本质就是让我们的组件不再使用 class 组件,所以,如果你的项目还在用 react 的 class 组件的方式,是不能使用 hook 的

react 也内置了一些对应的 hook,比如我们常用的 useState、useEffect 等等,这里就不多说了

让我们开始封装自己的一个 hook 库吧

useToggle

import { useState } from "react"

export default function useToggle(defaultValue) {
  const [value, setValue] = useState(defaultValue)

  function toggleValue(value) {
    setValue(currentValue =>
      typeof value === "boolean" ? value : !currentValue
    )
  }

  return [value, toggleValue]
}

通过代码能够看出这个 hook 的作用,本质就是进行状态的切换,你可以将其理解成一个 react 组件,也可以只是将其理解成一个函数,这个函数接受一个初始值,用 useState 进行状态的存储,通过函数 toggleValue 进行状态的切换,然后函数返回两个内容,一个是 当前的 value,一个是 toggleValue 函数,进行状态的切换,只不过组件返回的是一段 jsx 代码,这里返回的是一个数组

在使用方面就变的很简单了

export default function ToggleComponent() {
  const [value, toggleValue] = useToggle(false)

  return (
    <div>
      <div>{value.toString()}</div>
      <button onClick={toggleValue}>Toggle</button>
      <button onClick={() => toggleValue(true)}>Make True</button>
      <button onClick={() => toggleValue(false)}>Make False</button>
    </div>
  )
}

useStorage

前端的数据存储离不开 localStorage 和 sessionStorage,那如何根据这个内容写一个自定义 hook 呢?

import { useCallback, useState, useEffect } from "react"

export function useLocalStorage(key, defaultValue) {
  return useStorage(key, defaultValue, window.localStorage)
}

export function useSessionStorage(key, defaultValue) {
  return useStorage(key, defaultValue, window.sessionStorage)
}

function useStorage(key, defaultValue, storageObject) {
  const [value, setValue] = useState(() => {
    const jsonValue = storageObject.getItem(key)
    if (jsonValue != null) return JSON.parse(jsonValue)

    if (typeof defaultValue === "function") {
      return defaultValue()
    } else {
      return defaultValue
    }
  })

  useEffect(() => {
    if (value === undefined) return storageObject.removeItem(key)
    storageObject.setItem(key, JSON.stringify(value))
  }, [key, value, storageObject])

  const remove = useCallback(() => {
    setValue(undefined)
  }, [])

  return [value, setValue, remove]
}

这两个 hook 功能差不多,接收两个参数,key 和 defaultValue,当然你还可以扩展过期时间相关内容

useEffect 监听 key 或者 value 是否变化做出一系列操作,通过 JSON.stringify 格式化成字符串,并通过 value 是否是 undefined 进行删除操作

使用也比较简单

export default function StorageComponent() {
  const [age, setAge, removeAge] = useLocalStorage("age", 26)

  return (
    <div>
      <div>
        {name} - {age}
      </div>
      <button onClick={() => setAge(40)}>Set Age</button>
      <button onClick={removeAge}>Remove Age</button>
    </div>
  )
}

useAsync

import { useCallback, useEffect, useState } from "react"

export default function useAsync(callback, dependencies = []) {
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState()
  const [value, setValue] = useState()

  const callbackMemoized = useCallback(() => {
    setLoading(true)
    setError(undefined)
    setValue(undefined)
    callback()
      .then(setValue)
      .catch(setError)
      .finally(() => setLoading(false))
  }, dependencies)

  useEffect(() => {
    callbackMemoized()
  }, [callbackMemoized])

  return { loading, error, value }
}

主要内容还是针对 useState 和 useEffect 的封装,相互结合组成了 useAsync 的封装,callback 传入的是一个 Promise 函数,将 loading、error、value 统一处理,并针对 useEffect 的执行时机添加了 dependencies 参数

const { loading, error, value } = useAsync(() => {
  return new Promise((resolve, reject) => {
    const success = false
    setTimeout(() => {
      success ? resolve("Hi") : reject("Error")
    }, 1000)
  })
})

useFetch

根据我们封装的 useAsync,通过进一步处理,我们还能够得到更好用的 useFetch,之后在项目中再使用就不需要用自己封装的 fetch.js 了,毕竟其中没有 loading 或者 value 绑定在 state 的操作,可以用更好用的 useFetch

const DEFAULT_OPTIONS = {
  headers: { "Content-Type": "application/json" },
}

export default function useFetch(url, options = {}, dependencies = []) {
  return useAsync(() => {
    return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then(res => {
      if (res.status === 200) return res.data
      return Promise.reject(res)
    })
  }, dependencies)
}

使用方式

const { loading, error, value } = useFetch(
    url,
    {
      method: 'post'
    }
  )

useEffectOnce

这个实现起来比较简单

import { useEffect } from "react"

export default function useEffectOnce(cb) {
  useEffect(cb, [])
}

使用同样

useEffectOnce(() => alert("Hi"))

useRenderCount

查看某个页面渲染了多少次

import { useEffect, useRef } from "react"

export default function useRenderCount() {
  const count = useRef(1)
  useEffect(() => count.current++)
  return count.current
}

使用

const renderCount = useRenderCount()

useTimeout

import { useCallback, useEffect, useRef } from "react"

export default function useTimeout(callback, delay) {
  const callbackRef = useRef(callback)
  const timeoutRef = useRef()

  useEffect(() => {
    callbackRef.current = callback
  }, [callback])

  const set = useCallback(() => {
    timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
  }, [delay])

  const clear = useCallback(() => {
    timeoutRef.current && clearTimeout(timeoutRef.current)
  }, [])

  useEffect(() => {
    set()
    return clear
  }, [delay, set, clear])

  const reset = useCallback(() => {
    clear()
    set()
  }, [clear, set])

  return { reset, clear }
}

这个 hook 本质就是延迟多长时间执行 callback 函数,对外暴露了两个方法,分别是重置 reset 和 clear 清除定时器,可以更方便进行定时器操作,使用 ref 保存定时器和回调函数

使用方式

const { clear, reset } = useTimeout(() => setCount(0), 1000)

通过按钮点击或者函数调用来对定时器进行操作

useDebounce

同样的,对 useTimeout 进一步进行封装,可以实现 debounce 的操作,主要目的是为了解决某个方法在指定时间内重复调用,用 hook 的方式可以很方便的解决这种问题

export default function useDebounce(callback, delay, dependencies) {
  const { reset, clear } = useTimeout(callback, delay)
  useEffect(reset, [...dependencies, reset])
  useEffect(clear, [])
}

其中通过 dependencies 的变化可以控制 reset,控制执行的频率

const [count, setCount] = useState(10)
useDebounce(() => alert(count), 1000, [count])

count 在 1s 之内变化频繁的话,是不会触发 alert 的,当然也可以通过一个是否立即执行的参数进行一些相应的控制,这里就不提了,有兴趣的同学可以自主完善一下

总结

总体来看,封装 hook 还是挺简单的,你可以理解为就是把一些常用的原生的 hook 或者一些函数的再次封装,结合 state 或者 effect 将一些通用的逻辑提取,让页面变化更简单,更专注于页面本身自己的逻辑

同时也需要注意 hook 的一些使用规则,本质它就是一个 js 函数

  1. 只能在函数最外层调用 hook,不要在循环、条件判断或者子函数中调用
  2. 只能在 React 的函数组件中调用 hook 不要在其他 JavaScript 函数中调用,当然你也可以在自定义函数中调用自定义 hook,比如我们实现的 useFetch 就是基于 useAsync
发布于 2023-01-27 23:12・IP 属地陕西

标签:function,const,手把手,value,hook,hooks,return,useEffect
From: https://www.cnblogs.com/sexintercourse/p/17167351.html

相关文章

  • React Hooks用法大全
    ReactHooks用法大全 前言在React的世界中,有容器组件和UI组件之分,在ReactHooks出现之前,UI组件我们可以使用函数,无状态组件来展示UI,而对于容器组件,函数组件......
  • 彻底搞懂React-hook链表构建原理
    写在前面的小结每一个hook函数都有对应的hook对象保存状态信息useContext是唯一一个不需要添加到hook链表的hook函数只有useEffect、useLayoutEffect以及us......
  • HOOK大法
    //请求头hook测试网站同花顺OKvarheader_old=window.XMLHttpRequest.prototype.setRequestHeader;window.XMLHttpRequest.prototype.setRequestHeader=function......
  • inlineHook 注入
    inject.h#pragmaonce#ifndefINJECT_H#defineINJECT_Hintinject_sc();#endif//!INJECT_H#include<windows.h>#include<stdio.h>#include<iostream>......
  • k8s operator添加webhook
    k8soperator添加webhookOperator中的webhook,其作用与过滤器类似,外部对CRD资源的变更,在Controller处理之前都会交给webhook提前处理,kubernetes官方博客明确指出webhoo......
  • 手把手带你体验ChatGPT
    1、ChatGPT介绍ChatGPT,OpenAI研发的聊天机器人程序,于2022年11月30日发布。ChatGPT是人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人类的语言来进行对话,还......
  • 单元测试利器——手把手教你使用Mockito
    作者:京东零售秦浩然从你成为开发人员的那一天起,写单元测试终究是你逃不开的宿命!那开发人员为什么不喜欢写单元测试呢?究其原因,无外乎是依赖。依赖其他的服务、依赖运行的环......
  • 手把手教你为基于Netty的IM生成自签名SSL/TLS证书
    1、引言对于IM聊天应用来说,为了提升安全性,对聊天消息加密是常规操作。众所周之,Netty是高性能的JavaNIO网络通信框架,因而用Netty来写IM是再正常不过了。网上关于为Netty......
  • cert manager搭配alidns-webhook实现ingress证书签发
    安装certmanager如果已经安装certmanager则不用执行这一步helminstall\cert-managerjetstack/cert-manager\--namespacecert-manager\--create-name......
  • ChatGPT手把手教你配置及使用,防踩坑
    一、为何ChatGPT会如此火?分析 ChatGPT 背后火的原因:微软大佬站台+马斯克名人宣传,本身就会带来很大的流量;而 ChatGPT 本身也是非常给力,发布了 GPT-3 模型,取名达芬奇......