什么是 ahooks?
ahooks 是一个 React Hooks 库,提供了大量实用的自定义 hooks,帮助开发者更高效地构建 React 应用。其中副作用类 hooks 是 ahooks 的一个重要分类,专门用于处理各种副作用操作,如定时器、防抖、节流等。
安装 ahooks
npm install ahooks
副作用类 hooks 详解
useUpdateEffect – 更新时执行副作用
useUpdateEffect
在依赖项更新时执行副作用,跳过首次执行。
import React, { useState } from "react";
import { useUpdateEffect } from "ahooks";
import { Button, Card } from "antd";
const UseUpdateEffectExample = () => {
const [count, setCount] = useState(0);
const [updateCount, setUpdateCount] = useState(0);
useUpdateEffect(() => {
console.log("依赖项更新,执行副作用");
setUpdateCount((prev) => prev + 1);
}, [count]);
return (
<Card title="useUpdateEffect 更新时执行副作用">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p>
<strong>更新次数:</strong> {updateCount}
</p>
</div>
<Button onClick={() => setCount(count + 1)}>增加计数</Button>
</Card>
);
};
useUpdateLayoutEffect – 更新时执行布局副作用
useUpdateLayoutEffect
在依赖项更新时执行布局副作用,跳过首次执行。
import React, { useState, useRef } from "react";
import { useUpdateLayoutEffect } from "ahooks";
import { Button, Card } from "antd";
const UseUpdateLayoutEffectExample = () => {
const [count, setCount] = useState(0);
const divRef = useRef(null);
useUpdateLayoutEffect(() => {
if (divRef.current) {
divRef.current.style.backgroundColor =
count % 2 === 0 ? "#f0f0f0" : "#e6f7ff";
}
}, [count]);
return (
<Card title="useUpdateLayoutEffect 更新时执行布局副作用">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<div
ref={divRef}
style={{
padding: 16,
border: "1px solid #d9d9d9",
borderRadius: 4,
transition: "background-color 0.3s",
}}
>
这个div的背景色会在计数更新时改变
</div>
</div>
<Button onClick={() => setCount(count + 1)}>切换背景色</Button>
</Card>
);
};
useAsyncEffect – 异步副作用
useAsyncEffect
用于处理异步副作用操作。
import React, { useState } from "react";
import { useAsyncEffect } from "ahooks";
import { Button, Card } from "antd";
const UseAsyncEffectExample = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
useAsyncEffect(async () => {
setLoading(true);
try {
// 模拟异步请求
await new Promise((resolve) => setTimeout(resolve, 1000));
setData("异步数据加载完成");
} catch (error) {
setData("加载失败");
} finally {
setLoading(false);
}
}, []);
return (
<Card title="useAsyncEffect 异步副作用">
<div style={{ marginBottom: 16 }}>
<p>
<strong>状态:</strong> {loading ? "加载中..." : data}
</p>
</div>
<Button disabled={loading}>组件挂载时自动加载数据</Button>
</Card>
);
};
useDebounceEffect – 防抖副作用
useDebounceEffect
创建防抖的副作用,延迟执行。
import React, { useState } from "react";
import { useDebounceEffect } from "ahooks";
import { Input, Card } from "antd";
const UseDebounceEffectExample = () => {
const [value, setValue] = useState("");
const [debouncedValue, setDebouncedValue] = useState("");
useDebounceEffect(
() => {
setDebouncedValue(value);
console.log("防抖后的值:", value);
},
[value],
{ wait: 500 }
);
return (
<Card title="useDebounceEffect 防抖副作用">
<div style={{ marginBottom: 16 }}>
<Input
placeholder="输入内容(500ms 防抖)"
value={value}
onChange={(e) => setValue(e.target.value)}
style={{ marginBottom: 8 }}
/>
<p>
<strong>实时值:</strong> {value}
</p>
<p>
<strong>防抖值:</strong> {debouncedValue}
</p>
</div>
</Card>
);
};
useDebounceFn – 防抖函数
useDebounceFn
创建防抖函数。
import React, { useState } from "react";
import { useDebounceFn } from "ahooks";
import { Button, Card } from "antd";
const UseDebounceFnExample = () => {
const [count, setCount] = useState(0);
const { run } = useDebounceFn(
() => {
setCount((prev) => prev + 1);
},
{ wait: 1000 }
);
return (
<Card title="useDebounceFn 防抖函数">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
快速点击按钮,只有最后一次点击会在1秒后生效
</p>
</div>
<Button onClick={run}>防抖增加计数</Button>
</Card>
);
};
useThrottleFn – 节流函数
useThrottleFn
创建节流函数。
import React, { useState } from "react";
import { useThrottleFn } from "ahooks";
import { Button, Card } from "antd";
const UseThrottleFnExample = () => {
const [count, setCount] = useState(0);
const { run } = useThrottleFn(
() => {
setCount((prev) => prev + 1);
},
{ wait: 1000 }
);
return (
<Card title="useThrottleFn 节流函数">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
快速点击按钮,每秒最多执行一次
</p>
</div>
<Button onClick={run}>节流增加计数</Button>
</Card>
);
};
useThrottleEffect – 节流副作用
useThrottleEffect
创建节流的副作用。
import React, { useState } from "react";
import { useThrottleEffect } from "ahooks";
import { Input, Card } from "antd";
const UseThrottleEffectExample = () => {
const [value, setValue] = useState("");
const [throttledValue, setThrottledValue] = useState("");
useThrottleEffect(
() => {
setThrottledValue(value);
console.log("节流后的值:", value);
},
[value],
{ wait: 1000 }
);
return (
<Card title="useThrottleEffect 节流副作用">
<div style={{ marginBottom: 16 }}>
<Input
placeholder="输入内容(1000ms 节流)"
value={value}
onChange={(e) => setValue(e.target.value)}
style={{ marginBottom: 8 }}
/>
<p>
<strong>实时值:</strong> {value}
</p>
<p>
<strong>节流值:</strong> {throttledValue}
</p>
</div>
</Card>
);
};
useDeepCompareEffect – 深度比较副作用
useDeepCompareEffect
使用深度比较来决定是否执行副作用。
import React, { useState } from "react";
import { useDeepCompareEffect } from "ahooks";
import { Button, Card } from "antd";
const UseDeepCompareEffectExample = () => {
const [obj, setObj] = useState({ name: "张三", age: 25 });
const [count, setCount] = useState(0);
useDeepCompareEffect(() => {
console.log("对象深度比较后发生变化:", obj);
setCount((prev) => prev + 1);
}, [obj]);
return (
<Card title="useDeepCompareEffect 深度比较副作用">
<div style={{ marginBottom: 16 }}>
<p>
<strong>对象:</strong> {JSON.stringify(obj)}
</p>
<p>
<strong>执行次数:</strong> {count}
</p>
</div>
<div>
<Button
onClick={() => setObj({ name: "张三", age: 25 })}
style={{ marginRight: 8 }}
>
设置相同对象(浅比较会触发,深度比较不会)
</Button>
<Button onClick={() => setObj({ name: "李四", age: 30 })}>
设置不同对象
</Button>
</div>
</Card>
);
};
useDeepCompareLayoutEffect – 深度比较布局副作用
useDeepCompareLayoutEffect
使用深度比较来决定是否执行布局副作用。
import React, { useState, useRef } from "react";
import { useDeepCompareLayoutEffect } from "ahooks";
import { Button, Card } from "antd";
const UseDeepCompareLayoutEffectExample = () => {
const [config, setConfig] = useState({ width: 100, height: 100 });
const divRef = useRef(null);
useDeepCompareLayoutEffect(() => {
if (divRef.current) {
divRef.current.style.width = `${config.width}px`;
divRef.current.style.height = `${config.height}px`;
}
}, [config]);
return (
<Card title="useDeepCompareLayoutEffect 深度比较布局副作用">
<div style={{ marginBottom: 16 }}>
<div
ref={divRef}
style={{
backgroundColor: "#1890ff",
transition: "all 0.3s",
}}
/>
<p>
<strong>配置:</strong> {JSON.stringify(config)}
</p>
</div>
<div>
<Button
onClick={() => setConfig({ width: 100, height: 100 })}
style={{ marginRight: 8 }}
>
设置相同配置
</Button>
<Button onClick={() => setConfig({ width: 200, height: 150 })}>
设置不同配置
</Button>
</div>
</Card>
);
};
useInterval – 定时器
useInterval
创建定时器。
import React, { useState } from "react";
import { useInterval } from "ahooks";
import { Button, Card } from "antd";
const UseIntervalExample = () => {
const [count, setCount] = useState(0);
const [delay, setDelay] = useState(1000);
useInterval(() => {
setCount((prev) => prev + 1);
}, delay);
return (
<Card title="useInterval 定时器">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p>
<strong>间隔:</strong> {delay}ms
</p>
</div>
<div>
<Button onClick={() => setDelay(1000)} style={{ marginRight: 8 }}>
1秒间隔
</Button>
<Button onClick={() => setDelay(2000)} style={{ marginRight: 8 }}>
2秒间隔
</Button>
<Button onClick={() => setDelay(null)}>停止</Button>
</div>
</Card>
);
};
useRafInterval – RAF 定时器
useRafInterval
使用 requestAnimationFrame 创建定时器。
import React, { useState } from "react";
import { useRafInterval } from "ahooks";
import { Button, Card } from "antd";
const UseRafIntervalExample = () => {
const [count, setCount] = useState(0);
const [delay, setDelay] = useState(1000);
useRafInterval(() => {
setCount((prev) => prev + 1);
}, delay);
return (
<Card title="useRafInterval RAF 定时器">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p>
<strong>间隔:</strong> {delay}ms
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
使用 requestAnimationFrame,性能更好
</p>
</div>
<div>
<Button onClick={() => setDelay(1000)} style={{ marginRight: 8 }}>
1秒间隔
</Button>
<Button onClick={() => setDelay(2000)} style={{ marginRight: 8 }}>
2秒间隔
</Button>
<Button onClick={() => setDelay(null)}>停止</Button>
</div>
</Card>
);
};
useTimeout – 延时器
useTimeout
创建延时器。
import React, { useState } from "react";
import { useTimeout } from "ahooks";
import { Button, Card } from "antd";
const UseTimeoutExample = () => {
const [message, setMessage] = useState("");
useTimeout(() => {
setMessage("3秒后显示的消息");
}, 3000);
return (
<Card title="useTimeout 延时器">
<div style={{ marginBottom: 16 }}>
<p>
<strong>消息:</strong> {message || "等待中..."}
</p>
</div>
<Button disabled>组件挂载3秒后显示消息</Button>
</Card>
);
};
useRafTimeout – RAF 延时器
useRafTimeout
使用 requestAnimationFrame 创建延时器。
import React, { useState } from "react";
import { useRafTimeout } from "ahooks";
import { Button, Card } from "antd";
const UseRafTimeoutExample = () => {
const [message, setMessage] = useState("");
useRafTimeout(() => {
setMessage("2秒后显示的消息(RAF)");
}, 2000);
return (
<Card title="useRafTimeout RAF 延时器">
<div style={{ marginBottom: 16 }}>
<p>
<strong>消息:</strong> {message || "等待中..."}
</p>
<p style={{ fontSize: "12px", color: "#666" }}>
使用 requestAnimationFrame,性能更好
</p>
</div>
<Button disabled>组件挂载2秒后显示消息</Button>
</Card>
);
};
useLockFn – 锁函数
useLockFn
创建锁函数,防止重复执行。
import React, { useState } from "react";
import { useLockFn } from "ahooks";
import { Button, Card } from "antd";
const UseLockFnExample = () => {
const [loading, setLoading] = useState(false);
const [count, setCount] = useState(0);
const handleClick = useLockFn(async () => {
setLoading(true);
// 模拟异步操作
await new Promise((resolve) => setTimeout(resolve, 2000));
setCount((prev) => prev + 1);
setLoading(false);
});
return (
<Card title="useLockFn 锁函数">
<div style={{ marginBottom: 16 }}>
<p>
<strong>计数:</strong> {count}
</p>
<p>
<strong>状态:</strong> {loading ? "执行中..." : "空闲"}
</p>
</div>
<Button onClick={handleClick} loading={loading}>
点击执行(2秒内重复点击无效)
</Button>
</Card>
);
};
useUpdate – 强制更新
useUpdate
强制组件重新渲染。
import React from "react";
import { useUpdate } from "ahooks";
import { Button, Card } from "antd";
const UseUpdateExample = () => {
const update = useUpdate();
return (
<Card title="useUpdate 强制更新">
<div style={{ marginBottom: 16 }}>
<p>
<strong>渲染时间:</strong> {new Date().toLocaleTimeString()}
</p>
</div>
<Button onClick={update}>强制重新渲染</Button>
</Card>
);
};
副作用类 hooks 速查表
Hook 名称 | 用途 | 描述 |
---|---|---|
useUpdateEffect | 更新时执行副作用 | 在依赖项更新时执行,跳过首次执行 |
useUpdateLayoutEffect | 更新时执行布局副作用 | 在依赖项更新时执行布局副作用 |
useAsyncEffect | 异步副作用 | 处理异步副作用操作 |
useDebounceEffect | 防抖副作用 | 创建防抖的副作用,延迟执行 |
useDebounceFn | 防抖函数 | 创建防抖函数 |
useThrottleFn | 节流函数 | 创建节流函数 |
useThrottleEffect | 节流副作用 | 创建节流的副作用 |
useDeepCompareEffect | 深度比较副作用 | 使用深度比较决定是否执行副作用 |
useDeepCompareLayoutEffect | 深度比较布局副作用 | 使用深度比较决定是否执行布局副作用 |
useInterval | 定时器 | 创建定时器 |
useRafInterval | RAF 定时器 | 使用 requestAnimationFrame 创建定时器 |
useTimeout | 延时器 | 创建延时器 |
useRafTimeout | RAF 延时器 | 使用 requestAnimationFrame 创建延时器 |
useLockFn | 锁函数 | 创建锁函数,防止重复执行 |
useUpdate | 强制更新 | 强制组件重新渲染 |
原文链接:https://code.ifrontend.net/archives/787,转载请注明出处。
评论0