通过8个常用hook手把手教你封装hooks |
您所在的位置:网站首页 › 常用快捷键15个 › 通过8个常用hook手把手教你封装hooks |
对于使用 react 的同学来说,hook 一定不陌生,但是如何封装 hook 以及在业务中怎么使用封装的 hook,很多同学并没有一个很好的实践,这篇文章就通过10个常用的 hook 让大家学会封装 hook,能够在自己的业务中使用,提高复用率,减少开发成本 前沿hook 到底有什么作用呢?它可以让你对一些功能组件重复逻辑进行封装,分离组件并将其功能细化,让组件逻辑变的简单明了,逻辑共享变的更容易,减少了代码的重复性,维护和更新变的更简单易懂 hook 的本质就是让我们的组件不再使用 class 组件,所以,如果你的项目还在用 react 的 class 组件的方式,是不能使用 hook 的 react 也内置了一些对应的 hook,比如我们常用的 useState、useEffect 等等,这里就不多说了 让我们开始封装自己的一个 hook 库吧 useToggleimport { 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 ( {value.toString()} Toggle toggleValue(true)}>Make True toggleValue(false)}>Make False ) }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 ( {name} - {age} setAge(40)}>Set Age Remove Age ) }useAsyncimport { 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()useTimeoutimport { 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 函数 只能在函数最外层调用 hook,不要在循环、条件判断或者子函数中调用只能在 React 的函数组件中调用 hook 不要在其他 JavaScript 函数中调用,当然你也可以在自定义函数中调用自定义 hook,比如我们实现的 useFetch 就是基于 useAsync |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |