本文已参与「新人创作礼」活动,一起开启掘金创作之路。
大家好,我是小杜杜,有的时候我们要做活动的时候常常会有倒计时的功能,或者是一些常见的副作用,节流或防抖,而这些副作用可以通过一个简单的Hook就能实现,接下来将逐渐讲解
学习ahooks需要你抱着这样的态度去学习:你可以不会,但你不能不知道,万一哪天用到了呢
本文介绍:setInterval、setInterval、useCountDown、useDebounce、useDebounceFn、useThrottle、useThrottleFn、useLockFn 9个Hook
跟之前一样我们通过 ⭐️ 做个标记,说明钩子的重要性
⭐️:一般; ⭐️⭐️:重要; ⭐️⭐️⭐️:很重要
在所介绍的这些钩子中,尤其注意 useReactive
这个钩子,相信他能给予你很多帮助(● ̄(エ) ̄●)
演示示例: Domesy/ahook
useInterval 管理 setInterval 的函数
推荐指数 ⭐️⭐️ useInterval:简单的理解就是,每隔一段时间就执行的函数,类似于轮询
- fn:重复调用的函数
- delay:间隔时间,当值为 null 或 undefined 会停止计时器
- option:对象,包含 immediate(是否在首次进行渲染,默认为false)
代码示例
import React, { useState } from 'react';
import { Button } from 'antd';
import { useInterval } from 'ahooks';
const Test: React.FC<any> = () => {
const [count, setCount] = useState(0);
useInterval(() => {
setCount(count + 1);
}, 1000);
return (
<>
<div style={{fontWeight: 'bolder'}}>基础用法:</div>
<div style={{marginTop: 8}}>每隔1000ms,数字加 1: {count}</div>
</>
);
}
const Mock: React.FC<any> = () => {
const [count, setCount] = useState(0);
const [interval, setInterval] = useState<number | null>(1000);
useInterval(
() => {
setCount(count + 1);
},
interval,
{ immediate: true },
);
return (
<>
<Test />
<div style={{fontWeight: 'bolder'}}>高阶用法:</div>
<div style={{marginTop: 8}}>每隔{interval || 0}ms,数字加 1: {count}</div>
<div style={{marginTop: 8}}>相隔时间:{interval}</div>
<div style={{marginTop: 8, display: 'flex', justifyContent: 'flex-start'}}>
<Button type='primary' style={{marginRight: 16}} onClick={() => {
if(typeof interval === 'number') setInterval(interval + 1000)
}}>加1s</Button>
<Button type='primary' style={{marginRight: 16}} onClick={() => setInterval(1000)}>重置</Button>
<Button type='primary' style={{marginRight: 16}} onClick={() => setInterval(null)}>清除</Button>
</div>
</>
);
};
export default Mock;
useTimeout 管理 setInterval 的函数
推荐指数 ⭐️⭐️
useTimeout:处理计时器函数的Hook
- fn:重复调用的函数
- delay:间隔时间,当值为 null 或 undefined 会停止计时器 代码示例:
import React, { useState } from 'react';
import { Button } from 'antd';
import { useTimeout } from 'ahooks';
const Mock: React.FC<any> = () => {
const [count, setCount] = useState(0);
useTimeout(() => {
setCount(count + 1);
}, 2000);
return (
<div>2000ms 后数字加1: {count}</div>
);
};
export default Mock;
useCountDown 倒计时
推荐指数 ⭐️⭐️⭐️
useCountDown
- 首先有两个值,一个是目标值(targetDate),一个是设置值,如果不设置初始值(setTargetDate),则默认为现在
- 会自动计算离目标值的天月周等(formattedRes),同时也有据目标值的时间戳(countdown),注意这里的单位是毫秒,所以精确到秒为 Math.round(countdown / 1000)
- 有常用的倒计时功能,并能控制停止时所触发的函数(onEnd)
代码示例
import React, { useEffect } from 'react';
import { Button, message } from 'antd';
import { useBoolean } from 'ahooks';
import { Method } from '@/utils';
const CountDownTest1: React.FC<any> = () => {
const [targetDate, setTargetDate] = useState<any>(Method.getDate({add: 3}));
const [countdown, formattedRes] = useCountDown({
targetDate,
});
useEffect(() => {
setTargetDate(Method.getDate({add: 2}))
}, [])
const { days, hours, minutes, seconds, milliseconds } = formattedRes;
return <div>设置时间为后天,目标时间为大后天:{days} 天, {hours} 小时 {minutes} 分钟 {seconds} 秒 {milliseconds}</div>
}
const CountDownTest2: React.FC<any> = () => {
const [targetDate, setTargetDate] = useState<any>(Method.getDate({add: 3}));
const [countdown, formattedRes] = useCountDown({
targetDate,
onEnd: () => {
message.info('已停止!')
}
});
return <div style={{display: 'flex',justifyContent: 'flex-start'}}>
倒计时:
<Button style={{margin: '0 24px'}} type='primary' onClick={() => {
setTargetDate(Date.now() + 60000);
}}>{countdown === 0 ? '倒计时' : “Math.round(countdown / 1000)}s“</Button>
<Button onClick={() => {
setTargetDate(undefined);
}}>停止 </Button>
</div>
}
const CountDownTest1: React.FC<any> = () => {
const [targetDate, setTargetDate] = useState<any>(Method.getDate({add: 3}));
const [countdown, formattedRes] = useCountDown({
targetDate,
});
useEffect(() => {
setTargetDate(Method.getDate({add: 2}))
}, [])
const { days, hours, minutes, seconds, milliseconds } = formattedRes;
return <div>设置时间为后天,目标时间为大后天:{days} 天, {hours} 小时 {minutes} 分钟 {seconds} 秒 {milliseconds}</div>
}
const CountDownTest2: React.FC<any> = () => {
const [targetDate, setTargetDate] = useState<any>(Method.getDate({add: 3}));
const [countdown, formattedRes] = useCountDown({
targetDate,
onEnd: () => {
message.info('已停止!')
}
});
return <div style={{display: 'flex',justifyContent: 'flex-start'}}>
倒计时:
<Button style={{margin: '0 24px'}} type='primary' onClick={() => {
setTargetDate(Date.now() + 60000);
}}>{countdown === 0 ? '倒计时' : Math.round(countdown / 1000)}s</Button>
<Button onClick={() => {
setTargetDate(undefined);
}}>停止 </Button>
</div>
}
const Mock: React.FC<any> = () => {
const [countdown, formattedRes] = useCountDown({
targetDate: Method.getDate({add: 1}),
});
const { days, hours, minutes, seconds, milliseconds } = formattedRes;
return (
<>
<div>距离下一天0点的数据:{days} 天, {hours} 小时 {minutes} 分钟 {seconds} 秒 {milliseconds}</div>
<div></div>
<CountDownTest1 />
<div></div>
<CountDownTest2 />
</>
};
export default Mock;
useDebounce 对值的防抖
推荐指数 ⭐️
使用:const debouncedValue = useDebounce(value: any, { wait: number}])
value: 防抖的值, wait:超时时间
代码示例
import React, { useState } from 'react';
import { Button } from 'antd';
import { useDebounce } from 'ahooks';
const Mock: React.FC<any> = () => {
const [value, setValue] = useState<string>('');
const debouncedValue = useDebounce(value, { wait: 500 });
return (
<div>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Typed value"
style={{ width: 280 }}
/>
<p style={{ marginTop: 16 }}>500ms后才会变化: {debouncedValue}</p>
</div>
);
};
export default Mock;
useDebounceFn 对函数的防抖
推荐指数 ⭐️
useDebounceFn:对函数进行防抖的hook,频繁调用run方法,但只会执行最后一遍
使用方法:const { run, cancel, flush} = useDebounceFn(fn: (...args:any) => any, { wait: number})
run(防抖进行的函数)、cancel(取消当前防抖)、flush(当前防抖立即调用)
代码示例
import React, { useState } from 'react';
import { Button } from 'antd';
import { useDebounceFn } from 'ahooks';
const Mock: React.FC<any> = () => {
const [value, setValue] = useState(0);
const { run } = useDebounceFn(
() => {
setValue(value + 1);
},
{
wait: 500,
},
);
return (
<div>
<p style={{ marginTop: 16 }}> 有效点击次数: {value} </p>
<Button type="primary" onClick={() => {run()}}>快速点击</Button>
</div>
);
};
export default Mock;
useThrottle 对值的节流
推荐指数 ⭐️
useThrottle:对值进行节流的hook频繁调用run方法,但只会执行最后一遍
使用方法: const throttledValue = useThrottle(value: any, { wait: number})
代码示例:
import React, { useState } from 'react';
import { Button } from 'antd';
import { useThrottle } from 'ahooks';
const Mock: React.FC<any> = () => {
const [value, setValue] = useState<string>();
const throttledValue = useThrottle(value, { wait: 500 });
return (
<div>
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="输入值"
style={{ width: 280 }}
/>
<p style={{ marginTop: 16 }}>每隔 500ms 变化一次: {throttledValue}</p>
</div>
);
};
export default Mock;
useThrottleFn 对函数的节流
推荐指数 ⭐️
useThrottleFn:对函数进行节流的hook,频繁调用 run,但只会每隔 500ms 执行一次相关函数。
使用方法: const { run, cancel, flush} = useThrottleFn(fn: (...args:any) => any, { wait: number})
run(节流进行的函数)、cancel(取消当前节流)、flush(当前节流立即调用)
代码示例
import React, { useState } from 'react';
import { Button } from 'antd';
import { useThrottleFn } from 'ahooks';
const Mock: React.FC<any> = () => {
const [value, setValue] = useState(0);
const { run } = useThrottleFn(
() => {
setValue(value + 1);
},
{ wait: 500 },
);
return (
<div>
<p style={{ marginBottom: 16 }}> Clicked count: {value} </p>
<Button type='primary' onClick={run}>快速点击</Button>
</div>
);
};
export default Mock;
useLockFn 静态锁
推荐指数 ⭐️⭐️⭐️
useLockFn:用于给一个异步函数增加竞态锁,防止并发执行。
我们在提交表单,订单的时候有可能会因为用户的误操作等进行重复提交,这是有的小伙伴说使用 防抖
,其实防抖并不是最佳的选择,最好的方式就是静态锁
,如果当前的方法不执行完,则不会再次有效,而不是像防抖一样有个几秒内的时间,所以这个钩子非常重要
代码示例
import React, { useState } from 'react';
import { Button } from 'antd';
import { useLockFn } from 'ahooks';
function mockApiRequest() {
return new Promise((resolve:any) => {
setTimeout(() => {
resolve();
}, 2000);
});
}
const Mock: React.FC<any> = () => {
const [count, setCount] = useState(0);
const submit = useLockFn(async () => {
message.info('开始执行')
await mockApiRequest();
setCount((v) => v + 1)
message.info('执行结束')
})
return (
<>
<div>点击次数:{count}</div>
<Button style={{marginTop: 8}} type='primary' onClick={submit} >点击</Button>
</>
);
};
export default Mock;