📖 核心概念
- useState:状态管理基础
- useEffect:副作用与生命周期
- useRef:持久化引用
- useMemo与useCallback:性能优化
- useContext:跨组件数据传递
- 自定义Hook:逻辑复用
💻 代码实现
核心Hooks实战
✅ tsx
import { useState, useEffect, useRef, useMemo, useCallback, useContext, createContext } from 'react';
// ===== useState:表单状态管理 =====
function useForm<T extends Record<string, any>>(initialValues: T) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
const handleChange = (name: keyof T, value: T[keyof T]) => {
setValues(prev => ({ ...prev, [name]: value }));
setErrors(prev => ({ ...prev, [name]: undefined }));
};
const reset = () => {
setValues(initialValues);
setErrors({});
};
return { values, errors, handleChange, setErrors, reset };
}
// ===== useEffect:数据获取与清理 =====
function useFetch<T>(url: string, options?: RequestInit) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [loading, setLoading] = useState(true);
const abortRef = useRef<AbortController | null>(null);
useEffect(() => {
abortRef.current?.abort();
const controller = new AbortController();
abortRef.current = controller;
setLoading(true);
fetch(url, { ...options, signal: controller.signal })
.then(r => { if (!r.ok) throw new Error(\`HTTP \${r.status}\`); return r.json(); })
.then(setData)
.catch(err => { if (err.name !== 'AbortError') setError(err); })
.finally(() => setLoading(false));
return () => controller.abort(); // 清理:取消请求
}, [url]);
return { data, error, loading };
}
// ===== useRef:DOM操作与定时器 =====
function Stopwatch() {
const [time, setTime] = useState(0);
const [running, setRunning] = useState(false);
const intervalRef = useRef<ReturnType<typeof setInterval>>();
useEffect(() => {
if (running) {
intervalRef.current = setInterval(() => setTime(t => t + 10), 10);
} else {
clearInterval(intervalRef.current);
}
return () => clearInterval(intervalRef.current);
}, [running]);
return (
<div>
<span>{(time / 1000).toFixed(2)}s</span>
<button onClick={() => setRunning(!running)}>
{running ? '暂停' : '开始'}
</button>
<button onClick={() => { setRunning(false); setTime(0); }}>重置</button>
</div>
);
}
性能Hooks与自定义Hook
✅ tsx
// ===== useMemo与useCallback =====
function SearchableList({ items }: { items: Item[] }) {
const [query, setQuery] = useState('');
const [sortBy, setSortBy] = useState<'name' | 'date'>('name');
// 只在query或items变化时重新计算
const filtered = useMemo(() =>
items.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
),
[items, query]
);
// 只在sortBy变化时重新排序
const sorted = useMemo(() =>
[...filtered].sort((a, b) =>
sortBy === 'name' ? a.name.localeCompare(b.name) : a.date - b.date
),
[filtered, sortBy]
);
return (
<div>
<input value={query} onChange={e => setQuery(e.target.value)} />
{sorted.map(item => <ItemRow key={item.id} item={item} />)}
</div>
);
}
// ===== useContext:主题系统 =====
interface Theme {
colors: { primary: string; bg: string; text: string };
mode: 'light' | 'dark';
toggle: () => void;
}
const ThemeContext = createContext<Theme>({} as Theme);
function ThemeProvider({ children }: { children: ReactNode }) {
const [mode, setMode] = useState<'light' | 'dark'>('dark');
const theme = useMemo(() => ({
mode,
toggle: () => setMode(m => m === 'dark' ? 'light' : 'dark'),
colors: mode === 'dark'
? { primary: '#06b6d4', bg: '#0f172a', text: '#e0e0e0' }
: { primary: '#0891b2', bg: '#ffffff', text: '#1e293b' },
}), [mode]);
return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>;
}
// ===== 自定义Hook:本地存储 =====
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
const set = useCallback((newValue: T | ((prev: T) => T)) => {
setValue(prev => {
const next = typeof newValue === 'function' ? (newValue as (p: T) => T)(prev) : newValue;
localStorage.setItem(key, JSON.stringify(next));
return next;
});
}, [key]);
return [value, set] as const;
}
// ===== 自定义Hook:窗口尺寸 =====
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const handler = () => setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
return size;
}
🎯 练习任务
- 1 实现useDebounce和useThrottle自定义Hook
- 2 创建一个useFetch Hook,支持缓存、重试和取消
- 3 用useContext实现多语言切换功能
- 4 实现一个useLocalStorage Hook,支持跨tab同步
🏆 成就解锁
Hooks大师 — 深入理解React Hooks,用自定义Hook复用逻辑