为什么会有 Hook

介绍Hooks之前,首先要给大家说一下React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类。。

但是我们知道,在以往开发中类组件纯函数组件的区别是很大的,纯函数组件有着类组件不具备的多种特点,简单列举几条

  • 纯函数组件没有状态
  • 纯函数组件没有生命周期
  • 纯函数组件没有this
  • 只能是纯函数

这就注定,我们所推崇的函数组件,只能做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux,但我们知道类组件的也是有缺点的,比如,遇到简单的页面,你的代码会显得很重,并且每创建一个类组件,都要去继承一个React实例,至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”,等等一系列的话。关于React类组件redux的作者又有话说

  • 大型组件很难拆分重构,也很难测试
  • 业务逻辑分散在组件的各个方法之中,导致重复逻辑关联逻辑
  • 组件类引入了复杂的编程模式,比如 render props高阶组件

类组件虽然功能齐全却很重,纯函数很轻便却有上文几点重大限制,所以 React 团队设计了 React Hooks,React Hooks 就是加强版的函数组件,我们可以完全不使用 class,就能写出一个全功能的组件

Hook 解决了什么问题

Hook 解决了我们五年来编写和维护成千上万的组件时遇到的各种各样看起来不相关的问题。无论你正在学习 React,或每天使用,或者更愿尝试另一个和 React 有相似组件模型的框架,你都可能对这些问题似曾相识。

React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store )。如果你使用过 React 一段时间,你也许会熟悉一些解决此类问题的方案,比如 render props高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。如果你在 React DevTools 中观察过 React 应用,你会发现由 providersconsumers高阶组件render props 等其他抽象层组成的组件会形成“嵌套地狱”。尽管我们可以在 DevTools 过滤掉它们,但这说明了一个更深层次的问题:React 需要为共享状态逻辑提供更好的原生途径。

你可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间社区内共享 Hook 变得更便捷。

官方提供了哪些 Hook

如果你刚开始接触 Hook,那么可能需要先自行查看 React 官方文档

  • 基础 Hook
    • useSstate
    • useEffect
    • useContext
  • 额外的 Hook
    • useReducer
    • useCallback
    • useMemo
    • useRef
    • useImperativeHandle
    • useLayoutEffect
    • useDebugValue

这篇文章的重点不会对官方 Hook 的使用做过多的讲解,当然,想要用好 Hook,首先要先把官方提供的这些 Hook 学明白,才能在项目开发的时候如鱼得水,其次就是做一些自定义 Hook 的封装,用来提高开发效率

当然,项目开发总不能遇到一个功能就去封装一个自定义 Hook ,这里为大家推荐两个比较好用的 Hooks 库: aHooks react-use,这两个库使用在项目开发中真的能大大提高开发效率,可以毫不夸张的说能够降低你的50%的业务代码,告别996

什么是 ahooks

官网: https://ahooks.js.org/zh-CN

ahooks 是一个 React Hooks 库,致力提供常用且高质量的 Hooks。 在使用之前,你需要掌握 ReactReact Hooks 基础用法。ahooks 是由蚂蚁 umi 团队、淘系 ice 团队以及阿里体育团队共同建设的 React Hooks 工具库。ahooks 基于 React Hooks 的逻辑封装能力,提供了大量常见好用的 Hooks,可以极大降低代码复杂度,提升开发效率。ahooks 致力成为和 antd/fusion 一样的 React 基础设施,帮助开发者在逻辑层面省去大量的重复工作。

安装

# 安装依赖
npm i ahooks --save

# 使用 Hooks
import { useRequest } from 'ahooks';

有哪些实用 Hooks

  • 🔥异步请求

    • useRequest — 一个强大的管理异步数据请求的 Hook
  • 🔥table

    • useAntdTable— 封装了常用的 antd Formantd Table 联动逻辑,并且同时支持 antd V3 和 V4
    • useFusionTable — 封装了常用的 Fusion FormFusion Table 联动逻辑。
  • 🔥视图类的

    • useDrag & useDrop — 一对帮助你处理在拖拽中进行数据转移的 hooks
    • useDynamicList — 一个帮助你管理列表状态,并能生成唯一 keyHook
    • useSelections — 常见联动 checkbox 逻辑封装,支持多选,单选,全选逻辑,还提供了是否选择,是否全选,是否半选的状态。
    • useVirtualList — 提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。
  • 🔥副作用

    • useDebounce — 用来处理防抖值的 Hook
    • useDebounceFn — 用来处理防抖函数的 Hook
    • useInterval — 一个可以处理 setInterval 的 Hook
    • useThrottle — 用来处理节流值的 Hook
    • useThrottleFn — 用来处理节流函数的 Hook
    • useTimeout — 一个可以处理 setTimeout 计时器函数的 Hook。
  • 🔥生命周期

    • useDebounceEffect — 为 useEffect 增加防抖的能力。
    • useMount — 只在组件 mount 时执行的 hook。
    • useThrottleEffect — 为 useEffect 增加节流的能力。
    • useTrackedEffect — 在 useEffect 的基础上,追踪触发 effect 的依赖变化。
    • useUnmount — 只在组件 unmount 时执行的 hook。
    • useUnmountedRef — 获取当前组件是否已经卸载的 hook,用于避免因组件卸载后更新状态而导致的内存泄漏
    • useUpdate — 强制组件重新渲染的 hook。
    • useUpdateEffect — 一个只在依赖更新时执行的 useEffect hook。
    • useUpdateLayoutEffect — 一个只在依赖更新时执行的 useLayoutEffect Hook。
  • 🔥状态

    • useUrlState — 一个同步组件内部状态和 query 参数的 hook。
    • useBoolean — 优雅的管理 boolean 值的 Hook。
    • useControllableValue — 使组件的状态即可以自己管理,也可以被外部控制
    • useCookieState — 一个可以将状态持久化存储在 cookie 中的 Hook 。
    • useCountDown — 一个用于管理倒计时的 Hook。
    • useCounter — 一个可以管理 count 的 Hook。
    • useHistoryTravel — 优雅的管理状态变化历史,可以快速在状态变化历史中穿梭 - 前进跟后退。
    • useLocalStorageState — 一个可以将状态持久化存储在 localStorage 中的 Hook 。
    • useMap — 一个可以管理 Map 类型状态的 Hook。
    • useNetwork — 一个可以管理网络连接状态的 Hook。
    • usePrevious — 保存上一次渲染时状态的 Hook。
    • useSessionStorageState — 一个可以将状态持久化存储在 sessionStorage 中的 Hook。
    • useSet — 一个可以管理 Set 类型状态的 Hook。
    • useSetState — 管理 object 类型 state 的 Hooks,用法与 class 组件的 this.setState 基本一致。
    • useToggle — 用于在两个状态值间切换的 Hook。
    • useWebSocket — 用于处理 WebSocket 的 Hook。
    • useWhyDidYouUpdate — 帮助开发者排查是什么改变导致了组件的 rerender
  • 🔥Dom 相关

    • useClickAway — 优雅的管理目标元素外点击事件的 Hook。
    • useDocumentVisibility — 可以获取页面可见状态的 Hook。
    • useEventListener — 优雅使用 addEventListener 的 Hook。
    • useEventTarget — 常见表单控件(通过 e.target.value 获取表单值) 的 onChangevalue 逻辑封装,支持自定义值转换和重置功能。
    • useExternal — 一个用于动态地向页面加载或卸载外部资源的 Hook。
    • useFavicon — 用于设置与切换页面 favicon
    • useFullscreen — 一个用于处理 dom 全屏的 Hook。
    • useHover — 一个用于追踪 dom 元素是否有鼠标悬停的 Hook。
    • useInViewport — 一个用于判断 dom 元素是否在可视范围之内的 Hook。
    • useKeyPress — 一个优雅的管理 keyupkeydown 键盘事件的 Hook,支持键盘组合键,定义键盘事件的 keykeyCode 别名输入 。
    • useMouse — 一个跟踪鼠标位置的 Hook。
    • useResponsive — 获取响应式信息。
    • useScroll — 获取元素的滚动状态。
    • useSize — 一个用于监听 dom 节点尺寸变化的 Hook。
    • useTextSelection — 实时获取用户当前选取的文本内容及位置。
    • useTitle — 用于设置页面标题的 Hook。
  • 🔥高级 Hook

    • useCreation — 是 useMemouseRef 的替代品,确保实例不会被重新创建。
    • useEventEmitter — 多组件间事件通知。
    • useLockFn — 给异步函数加锁,防止重复调用。
    • usePersistFn — 持久化 function 的 Hook。
    • useReactive — 提供一种数据响应式的操作体验,定义数据状态不需要写 useState , 直接修改属性即可刷新视图。
    • useSafeState — 用法与 React.useState 完全一样,但是在组件卸载后异步回调内的 setState 不再执行,避免因组件卸载后更新状态而导致的内存泄漏。

什么是 react-use

是一个国外大佬开发,目前 start 25.9K,github 地址: https://github.com/streamich/react-use

安装

# 安装依赖
npm i react-use --save

# 使用 Hooks
import { useBattery  } from 'react-use';

有哪些实用 Hooks

  • 🔥传感器

    • useBattery — 跟踪设备电池状态。
    • useGeolocation — 跟踪用户设备的地理位置状态。
    • useHover and useHoverDirty — 跟踪鼠标悬停某个元素的状态。
    • useIdle — 跟踪用户是否处于非活动状态。
    • useKey, useKeyPress, useKeyboardJs, 和 useKeyPressEvent — 追踪按键。
    • useLocation — 跟踪页面导航栏的位置状态。
    • useMedia — 跟踪 CSS 媒体查询的状态。
    • useMediaDevices — 跟踪连接的硬件设备的状态。
    • useMotion — 跟踪设备的运动传感器的状态。
    • useMouse and useMouseHovered — 跟踪鼠标位置的状态。
    • useNetwork — 跟踪用户的互联网连接状态。
    • useOrientation — 跟踪设备屏幕方向的状态。
    • usePageLeave — 当鼠标离开页面边界时触发。
    • useScroll — 跟踪 HTML 元素的滚动位置。
    • useSize — 跟踪 HTML 元素的维度。
    • useStartTyping — 检测用户何时开始输入。
    • useWindowScroll — 跟踪 窗口 滚动位置。
    • useWindowSize — 跟踪 窗口 尺寸。
  • 🔥用户界面

    • useAudio — 播放音频并公开其控件。
    • useClickAway — 当用户点击目标区域外时触发回调。
    • useCss — 动态调整 CSS。
    • useDrop and useDropArea — 跟踪文件,链接和复制粘贴。
    • useFullscreen — 全屏显示元素或视频。
    • useSpeech — 从文本字符串合成语音。
    • useVideo — 播放视频,跟踪其状态,以及公开播放控件。
    • useWait — UI 的复杂等待管理。
  • 🔥动画效果

    • useRaf — 在每个 requestAnimationFrame 上重新呈现组件。
    • useSpring — 根据弹簧动力学随时间插入数字。
    • useTimeout — 超时后返回true
    • useTween — 重新渲染组件,同时补间 0 到 1 之间的数字。
    • useUpdate — 返回一个回调,在调用时重新呈现组件。
  • 🔥副作用

    • useAsync — 解析一个 async 函数。
    • useAsyncFn — 异步函数的状态管理。
    • useAsyncRetryuseAsync 带有 retry()方法。
    • useBeforeUnload — 当用户尝试重新加载或关闭页面时显示浏览器警报。
    • useCopyToClipboard — 将文本复制到剪贴板。
    • useDebounce — 防抖函数。
    • useFavicon — 设置页面的favicon
    • useLocalStorage — 管理localStorage中的值。
    • useLockBodyScroll — 锁定body元素的滚动。
    • useSessionStorage — 管理sessionStorage中的值。
    • useThrottle and useThrottleFn — 节流一个函数。
    • useTitle — 设置页面标题。
  • 🔥生命周期

    • useEffectOnce — 仅运行一次的修改后的useEffect
    • useEvent — 订阅事件。
    • useLifecycles — 调用mountunmount回调。
    • useRefMounted — 跟踪组件是否已挂载。
    • usePromise — 仅在安装组件时解析promise
    • useLogger — 在组件经历生命周期时登录控制台。
    • useMount — 调用mount回调。
    • useUnmount — 调用unmount回调。
    • useUpdateEffect — 仅在更新时运行一个effect
    • useDeepCompareEffect — 运行一个effect通过深度比较其依赖项。
  • 🔥状态

    • createMemomemoized hooks的工厂钩子。
    • useGetSet — 返回状态 getter get() 而不是原始状态。
    • useGetSetState — 就像 useGetSetuseSetState 有个孩子。
    • useObservable — 跟踪Observable的最新值。
    • useSetState — 创建类似this.setStatesetState方法。
    • useToggle and useBoolean — 跟踪布尔值的状态。
    • useCounter and useNumber — 跟踪数字的状态。
    • useList — 跟踪数组的状态。
    • useMap — 跟踪对象的状态。

以上介绍的两个 Hooks 库,运用在项目开发中可以说是能够减少你的 50% 业务代码,真的告别996,利用更多的时间来学习新的知识,不要做业务开发的傀儡 🎃

当然在我们平时开发过程中,肯定会遇到一些项目中业务逻辑之类的封装或者是以上两个 Hooks 库没有涉及到的场景,这种情况下就需要自己去自定义 Hook 了,千万要记住: 不要因为自己的一时偷懒直接复制代码,复制一时爽,重构火葬场 🔥🔥🔥

自定义 Hook

官方介绍:自定义 Hook 只是将两个函数之间一些共同的代码提取到单独的函数中。自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。

没错,就是函数和函数之间的逻辑复用,但是必须要遵循 Hook 的设计约定,不遵循的话,由于无法判断某个函数是否包含对其内部 Hook 的调用,React 将无法自动检查你的 Hook 是否违反了 Hook 的规则。以下是本人封装的以 Modal 的形式来展示错误信息Hook,仅供参考:

import React, { useState, useEffect } from 'react'
import { Modal, Table } from 'antd'
import { ColumnsType } from 'antd/es/table'
interface IData {
  message: string
}

 // 截取两个字符串之间字符
  function str_substr(start, end, str) {
    // 获取开始下标和结束下标
    let firstIndex = str.indexOf(start)
    let lastIndex = str.lastIndexOf(end)
    return str.substring(firstIndex + 1, lastIndex)
  }

const useErrModal = (data: string) => {
  const [dataSource, setDataSource] = useState<Array<IData>>([])

  const tableContainer = () => (
    <Table size={'small'} dataSource={dataSource} columns={columns} pagination={false} />
  )
  const columns: ColumnsType<IData> = [
    {
      title: '错误明细',
      dataIndex: 'message',
      key: 'message'
    }
  ]

  // data 数据按照 [, ] 切割,数据事例 : "数据部分导入失败:[第1行数据检验失败原因:税收编码为必填项且必须为19位的有效编码, 第2行数据检验失败原因:税率为必填项且为0开头的小数且小数点后不能超过5位,税收编码为必填项且必须为19位的有效编码]"
  let splitStr = str_substr('[', ']', data)
  let arr = splitStr.split(', ')
  let list = []
  arr.map((item, key) => {
    list.push({
      message: item
    })
  })
  setDataSource(list)

  Modal.warning({
    title: '错误信息汇总',
    okText: '确定',
    content: tableContainer
  })
}

export default useErrModal

参考资料

[1]ahooks: https://ahooks.js.org/zh-CN

[2]react-use: https://github.com/streamich/react-use

[3]hooks 详解: https://www.jianshu.com/p/d600f749bb19

[4]hooks 官方文档: https://react.docschina.org/docs/hooks-intro.html

写在最后

公众号前端开发爱好者 专注分享 web 前端相关技术文章视频教程资源、热点资讯等,如果喜欢我的分享,给 🐟🐟 点一个 👍 或者关注 ➕ 都是对我最大的支持。

欢迎长按图片加好友,我会第一时间和你分享前端行业趋势面试资源学习途径等等。

关注公众号后,在首页:

  • 回复面试资料,获取最新大厂面试资料。
  • 回复React实战,获取 React 最新实战教程。
  • 回复Vue实战,获取 Vue 最新实战教程。
  • 更多教程资源应用尽有,欢迎关注获取
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐