一.什么是ahooks
ahooks是由阿里的前端团队开发,它是基于React Hooks 的工具库,致力提供常用且高质量的 Hooks,hooks的创建使得构建 React 组件时不再需要定义class类。从开发中得到证明,很早之前,React中使用的类架构,随着项目结构的庞大,类组件的维护让我们的开发变得繁琐。经常发现自己在编写难以分解的大型复杂组件,并且组件之间极度耦合,很难再去拆分成小组件,而且相关代码分布在多个生命周期方法中,使得阅读、维护和测试变得格外棘手。此外,我们还必须绑定方法以this确保可以在组件中访问到this。在处理高阶组件时会遇到过多的 prop 钻孔问题——也称为包装器地狱。由此,hooks在前端react开发中渐渐兴起,阿里的开发团队将在开发中使用的hooks进行封装,最终得到了一套完整的hooks类库,这便是ahooks的由来。
ahooks 的前身是蚂蚁开源的 @umijs/hooks,可以说 ahooks 是 umi hooks 的 2.0 版本。
二.学习文档
三.ahooks安装
3.1 下载
npm i ahooks 或者 yarn add ahooks
3.2 按需加载
可以通过以下的写法来按需加载 Hooks。
import useToggle from 'ahooks/es/useToggle';
注意:ahooks 默认支持基于 ES module 的 tree shaking,对于 js 部分,直接引入
import { useToggle } from 'ahooks'
也会有按需加载的效果。
如果你使用了 babel,那么可以使用 babel-plugin-import 来进行按需加载,加入这个插件后。你可以仍然这么写:
import { useToggle } from 'ahooks';
插件会帮你转换成 ahooks/es/useToggle
的写法。
四.基本使用
4.1 简单案例
以下是使用useToggle这个hooks对文字的切换案例
import React from 'react'
import { useToggle } from "ahooks";
const Index = (props) => {
const [state, { toggle }] = useToggle();
return (
<>
{
state?<p>显示</p>:<p>隐藏</p>
}
<p>
<button onClick={() => toggle()}>切换</button>
</p>
</>
)
}
export default Index
五.常用hooks
5.1 useRequest
这个hooks使用于发送请求,使用过umi的小伙伴知道,umi中发送请求就是使用的useRequest 它具有以下特性:
- 自动请求/手动请求
- SWR(stale-while-revalidate)
- 缓存/预加载
- 屏幕聚焦重新请求
- 轮询
- 防抖
- 节流
- 并行请求
- 依赖请求
- loading delay
- 分页
- 加载更多,数据恢复 + 滚动位置恢复
在下面这个例子中, useRequest 接收了一个异步函数
getSome
,在组件初次加载时, 自动触发该函数执行。同时 useRequest 会自动管理异步请求的loading
,data
,error
等状态。
import {useRequest } from 'ahooks';
const getSome = async () => {};
const { data, loading, run } = useRequest(getSome, {
debounceInterval: 500,
manual: true,
refreshDeps: [], // manual为false,可以按被动式触发
});
5.2 缓存hooks
5.2.1 useCookieState
import { useCookieState } from 'ahooks';
const [message, setMessage] = useCookieState('cookie-key');
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
5.2.2 useLocalStorageState
import { useLocalStorageState } from 'ahooks';
const [message, setMessage] = useLocalStorageState('store-key', 'default value');
<input
value={message || ''}
onChange={(e) => setMessage(e.target.value)}
/>
5.2.3 useSessionStorageState
import { useSessionStorageState } from 'ahooks';
const [message, setMessage] = useSessionStorageState('store-key', 'default-value');
<input
value={message}
onChange={(e) => {
setMessage(e.target.value);
}}
/>
5.3 性能优化hooks
5.3.1 useCreation
它是用来代替useMemo、useRef的钩子,因为useMemo不能保证一定不会被重新计算,useRef如果针对复杂对象,每次渲染都创建一次会很耗性能
import { useCreation } from 'ahooks';
const foo = useCreation(() => {number: Math.random()}, []);
5.3.2 useDebounceFn
这个钩子用于防抖
import { useDebounceFn } from 'ahooks';
const { run } = useDebounceFn(
() => console.log('test'),
{ wait: 500 },
);
<div>
<Button onClick={run}>
Click fast!
</Button>
</div>
5.3.3 useThrottleFn
这个钩子用于节流
import { useThrottleFn } from 'ahooks';
const { run } = useThrottleFn(
() => console.log('test'),
{ wait: 500 },
);
<div>
<Button onClick={run}>
Click fast!
</Button>
</div>
5.3.4 useInterval
这个钩子与定时器功能一致
import { useInterval } from 'ahooks';
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
<div>数字: {count}</div>
5.3.5 useVirtualList
提供虚拟化列表能力的 Hook,用于解决展示海量数据渲染时首屏渲染缓慢和滚动卡顿问题。
import { useVirtualList } from 'ahooks';
const { list, containerProps, wrapperProps } = useVirtualList(Array.from(Array(99999).keys()), {
overscan: 30, // 视区上、下额外展示的 dom 节点数量
itemHeight: 60, // 行高度,静态高度可以直接写入像素值,动态高度可传入函数
});
5.4 操作DOM的hooks
5.4.1 useClickAway
这个钩子可以确定一个区域,当鼠标点击区域外触发,可以做一些点击区域外的操作
import React,{useRef} from 'react'
import { useClickAway } from 'ahooks';
const Index=(props)=> {
const ref = useRef();
useClickAway(() => {
console.log('点击到div外部了')
}, ref);
return(
<div ref={ref} style={{width:'200px',height:'100px',background:'red',
lineHeight:'100px',textAlign:'center'}}> 这是一个区域内 </div>
)
}
export default Index
5.4.2 useDocumentVisibility
可以获取页面的可见状态,并且监听document 的可见状态
import React,{useEffect} from 'react'
import { useDocumentVisibility } from 'ahooks';
const documentVisibility = useDocumentVisibility();
useEffect(() => {
if (documentVisibility === 'visible') {
console.log('当前页面在可见状态');
} else {
console.log('当前页面不在可见状态');
}
}, [documentVisibility]);
5.4.3 useEventListener
这个钩子类似于dom操作中的addEventListener,可以给dom元素绑定事件 这个钩子可以传递三个参数 | 参数 | 说明 | 类型 | 默认值 | |-----------|------------|------------|--------| | eventName | 事件名称 |
string
| - | | handler | 处理函数 |Function
| - | | options | 设置(可选) |Options
| - | 在它的源码中是这么绑定元素的,也是利用了addEventListener
targetElement.addEventListener(eventName, eventListener, {
capture: options.capture,
once: options.once,
passive: options.passive,
});
使用
import React, {
useState,
useRef
} from 'react'
import {
useEventListener
} from 'ahooks';
const Index = (props) => {
const [value, setValue] = useState(0);
const clickHandler = () => {
setValue(value + 1);
};
const ref = useRef();
useEventListener('click', clickHandler, {
target: ref
});
const style = {
width: '100px',
height: '100px',
background: 'red',
lineHeight: '100px',
textAlign: 'center'
}
return (
<>
<p>这是一个数字:{value}</p>
<div ref = {ref} style = {style} > 加一 </div>
</>
)
}
export default Index
5.4.4 useInViewport
这个钩子可以观测到目标元素是否在可视范围内
import { useInViewport } from 'ahooks';
const ref = useRef();
const inViewPort = useInViewport(ref);
<div ref={ref}>
{inViewPort ? 'div在可视区' : 'div不在可视区'}
</div>
在源码中它首先使用了一个getBoundingClientRect观察目标元素得到位置,判断它的top,bottom,left,right是否在可视区域内,用IntersectionObserver去监听当目标元素的位置发生变化时,目标元素是否还在可视区域内
//这里使用了 [IntersectionObserver API],这个api可以自动"观察"元素是否可见
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
setInViewport(true);
} else {
setInViewport(false);
}
}
});
observer.observe(el as HTMLElement);
return () => {
observer.disconnect();
};
}, [target]);
5.4.5 useResponsive
这个钩子可以监听浏览器视口的大小,可以做一些屏幕适配功能
import React from 'react'
import { configResponsive, useResponsive } from 'ahooks';
const Index=(props)=> {
configResponsive({
small: 0,
middle: 800,
large: 1200,
});
const responsive = useResponsive();
return (
<>
<h1>改变浏览器视口的大小,判断对错 </h1>
{Object.keys(responsive).map((key) => (
<p key={key}>
{key} {responsive[key] ? '✔' : '✘'}
</p>
))}
</>
)
}
export default Index
5.4.6 useEventEmitte
这个钩子是用来实现事件订阅的
import { useEventEmitter } from 'ahooks';
// 事件队列
const focus$ = useEventEmitter<number>();
// 发送
focus$.emit(123);
// 订阅
focus$.useSubscription(value => {
console.log(value);
});