React 19 入门教程
自定义 Hook
本教程共 50 篇 · 第 32 篇 · 更新于 2026-07-29 · 约 7 分钟阅读
React自定义Hookuse开头逻辑复用状态逻辑
32. 自定义 Hook
本节目标:学会创建自定义 Hook,在组件间复用状态逻辑,让代码更干净、更好维护。
为什么要自定义 Hook
两个组件有相同的逻辑,复制粘贴是最差的选择。改一个忘了改另一个,bug 就这么来的。
// 两个组件重复的逻辑
function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => { /* 清理 */ };
}, []);
return <span>{isOnline ? '在线' : '离线'}</span>;
}
function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// 完全一样的代码又写了一遍...
}, []);
return <button disabled={!isOnline}>保存</button>;
}
提取自定义 Hook
把重复逻辑提取成一个 use 开头的函数:
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() { setIsOnline(true); }
function handleOffline() { setIsOnline(false); }
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
现在两个组件都变简洁了:
function StatusBar() {
const isOnline = useOnlineStatus();
return <span>{isOnline ? '在线' : '离线'}</span>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
return <button disabled={!isOnline}>保存</button>;
}
自定义 Hook 的本质
自定义 Hook 就是”用了其他 Hook 的函数”。它本身没有特殊能力,只是逻辑复用的一种方式。
命名规范
- 必须以
use开头:useOnlineStatus、useWindowSize、useLocalStorage - 遵循 Hook 规则:只在顶层调用,只在函数组件或自定义 Hook 里调用
- 命名要有意义:从名字能看出它做什么
实战:useLocalStorage
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
// 使用
function Settings() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
当前:{theme}
</button>
);
}
实战:useDebounce
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// 使用
function SearchBox() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
// debouncedQuery 在用户停止输入 300ms 后才更新
// 用它发请求,避免每次按键都请求
}
实战:useWindowSize
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
function handleResize() {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用
function ResponsiveComponent() {
const { width } = useWindowSize();
return <p>窗口宽度:{width}px</p>;
}
Hook 之间可以传递信息
自定义 Hook 内部可以调用其他 Hook,也可以调用其他自定义 Hook:
function useUser(id) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/users/${id}`)
.then(res => res.json())
.then(setUser);
}, [id]);
return user;
}
function useUserPosts(id) {
const user = useUser(id); // 调用另一个自定义 Hook
const [posts, setPosts] = useState([]);
useEffect(() => {
if (user) {
fetch(`/api/users/${user.id}/posts`)
.then(res => res.json())
.then(setPosts);
}
}, [user]);
return posts;
}
自定义 Hook 的返回值
可以返回任何类型,看使用场景:
// 返回单个值
function useOnlineStatus() { return isOnline; }
// 返回数组(类似 useState)
function useLocalStorage(key, init) { return [value, setValue]; }
// 返回对象(属性多时更清晰)
function useMousePosition() {
return { x, y };
}
// 返回带方法的对象
function useCounter(initial) {
const [count, setCount] = useState(initial);
const increment = () => setCount(c => c + 1);
const reset = () => setCount(initial);
return { count, increment, reset };
}
本节要点
- 自定义 Hook 以
use开头,用于复用状态逻辑 - 本质是”用了其他 Hook 的函数”
- Hook 之间可以互相调用
- 返回值灵活:单个值、数组、对象都行