/
Update
7 min read
中文 react 自定义hook
React 自定义 Hook 是什么?#
核心概念#
自定义Hook 是React的一种代码复用机制,让你可以:
- 提取组件中的状态逻辑
- 在多个组件间共享这些逻辑
- 保持组件简洁和可维护
简单类比#
| 类比 | 说明 |
|---|---|
| 工具函数 | 就像把常用工具放在工具箱里 |
| 乐高积木 | 把常用功能封装成可复用的积木 |
| 食谱配方 | 把做菜步骤写成配方,随时可用 |
基本语法#
1. 命名规则#
// 必须以 "use" 开头
function useMyHook() { } // ✅ 正确
function myHook() { } // ❌ 错误javascript2. 最简单的自定义Hook#
// useCounter.js - 计数器逻辑封装
import { useState } from 'react';
function useCounter(initialValue = 0) {
// 使用React内置Hook
const [count, setCount] = useState(initialValue);
// 封装业务逻辑
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initialValue);
// 返回状态和操作函数
return { count, increment, decrement, reset };
}
export default useCounter;javascript3. 在组件中使用#
import useCounter from './useCounter';
function MyComponent() {
// 使用自定义Hook,就像使用useState一样
const { count, increment, decrement, reset } = useCounter(10);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}javascript项目中的实际应用#
1. useSession - 会话管理#
// src/hooks/useSession.ts
export function useSession(
sessionManager: SessionManager,
setSessionManager: (value: SessionManager) => void
) {
// 路由处理
const { sessionId } = useParams();
const navigate = useNavigate();
// 初始化逻辑
useEffect(() => {
if (sessionManager.sessions.length === 0) {
// 创建默认会话...
}
}, []);
// 会话操作
const createNewSession = () => { /* ... */ };
const switchSession = (id: string) => { /* ... */ };
const deleteSession = (id: string) => { /* ... */ };
return {
currentSession,
createNewSession,
switchSession,
deleteSession,
};
}typescript使用场景:
// ChatSidebar.tsx
function ChatSidebar() {
const { currentSession, createNewSession, switchSession } = useSession(...);
return (
<div>
{sessions.map(s => (
<div key={s.id} onClick={() => switchSession(s.id)}>
{s.title}
</div>
))}
<button onClick={createNewSession}>新建会话</button>
</div>
);
}typescript2. useRemoteChat - 远程聊天#
// src/hooks/useRemoteChat.ts
export function useRemoteChat(
apiConfig: RemoteApiConfig,
sessionMessages: Message[],
updateCurrentSession: (messages: Message[]) => void
) {
const [loading, setLoading] = useState(false);
// 发送消息逻辑(包含批量更新、流式处理等)
async function handleSend(input: SendMessageInput): Promise<void> {
// 1. 构建请求
// 2. 乐观更新UI
// 3. 流式处理响应
// 4. 批量更新机制
}
return { loading, handleSend };
}typescript使用场景:
// ChatComposer.tsx
function ChatComposer() {
const { loading, handleSend } = useRemoteChat(...);
const onSubmit = async (text) => {
if (loading) return;
await handleSend({ text });
};
return (
<form onSubmit={onSubmit}>
<input disabled={loading} />
<button disabled={loading}>发送</button>
</form>
);
}typescript3. useLocalStorage - 本地存储#
// src/hooks/useLocalStorage.ts
export function useLocalStorage<T>(key: string, initialValue: T) {
// 从localStorage读取
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
// 更新时同步到localStorage
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}typescript使用场景:
// 保存用户主题偏好
const [theme, setTheme] = useLocalStorage('theme', 'light');
// 保存会话数据
const [sessions, setSessions] = useLocalStorage('sessions', []);typescript自定义Hook的优势#
1. 代码复用#
// ❌ 不使用Hook - 重复代码
function ComponentA() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
// ...
}
function ComponentB() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
// ... 重复的逻辑
}
// ✅ 使用自定义Hook - 复用逻辑
function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
function ComponentA() {
const { count, increment } = useCounter();
}
function ComponentB() {
const { count, increment } = useCounter();
}javascript2. 逻辑分离#
// ❌ 组件臃肿
function ChatComponent() {
// 状态管理 - 50行
// API调用 - 80行
// 错误处理 - 30行
// UI渲染 - 20行
// 总共180行,难以维护
}
// ✅ 逻辑分离
function useChatLogic() {
// 所有业务逻辑封装在这里
return { messages, sendMessage, loading, error };
}
function ChatComponent() {
const { messages, sendMessage, loading } = useChatLogic();
// 只关注UI渲染,简洁清晰
}javascript3. 测试友好#
// 可以单独测试Hook逻辑
test('useCounter', () => {
const { result } = renderHook(() => useCounter(10));
act(() => result.current.increment());
expect(result.current.count).toBe(11);
});javascript常用自定义Hook模式#
1. 数据获取#
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, [url]);
return { data, loading, error };
}javascript2. 表单处理#
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (e) => {
setValues({ ...values, [e.target.name]: e.target.value });
};
const handleSubmit = (onSubmit) => (e) => {
e.preventDefault();
onSubmit(values);
};
return { values, errors, handleChange, handleSubmit };
}javascript3. 防抖/节流#
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// 使用
const debouncedSearch = useDebounce(searchTerm, 500);javascript总结#
| 特性 | 说明 |
|---|---|
| 命名 | 必须以 use 开头 |
| 本质 | 函数,可以调用其他Hook |
| 目的 | 复用状态逻辑 |
| 优势 | 代码复用、逻辑分离、测试友好 |
| 规则 | 只能在函数组件或自定义Hook中使用 |
自定义Hook是React中最强大的代码组织工具之一,让复杂应用保持清晰和可维护!