什么是 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><divref={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 }}><Inputplaceholder="輸入內容(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 }}><Inputplaceholder="輸入內容(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><ButtononClick={() => 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 }}><divref={divRef}style={{backgroundColor: "#1890ff",transition: "all 0.3s",}}/><p><strong>配置:</strong> {JSON.stringify(config)}</p></div><div><ButtononClick={() => 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 | 強制更新 | 強制組件重新渲染 |
?React強大且靈活hooks庫——ahooks入門實踐之副作用類hook(effect)詳解 - 高質量源碼分享平臺-免費下載各類網站源碼與模板及前沿動態資訊