返回教程列表
CodeGogo
进阶
25 分钟
React Hooks 详解
深入理解 React Hooks,掌握 useEffect、useContext 等常用 Hooks
useEffect Hook
useEffect 用于处理副作用,如数据获取、订阅、手动修改 DOM 等。
基本用法
useEffect(() => {
// 副作用代码
document.title = `Count: ${count}`;
// 清理函数(可选)
return () => {
// 清理操作
};
}, [dependencies]); // 依赖数组
无依赖(每次渲染后执行)
useEffect(() => {
console.log('组件渲染后执行');
});
空依赖数组(仅挂载时执行一次)
useEffect(() => {
console.log('组件挂载时执行');
return () => {
console.log('组件卸载时执行');
};
}, []);
有依赖(依赖变化时执行)
useEffect(() => {
console.log('count 变化:', count);
}, [count]);
数据获取示例
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 定义异步函数
const fetchUser = async () => {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (error) {
console.error('Error:', error);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <div>加载中...</div>;
if (!user) return <div>用户不存在</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
useContext Hook
用于跨组件层级共享数据,避免 prop drilling。
创建 Context
import { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
// 自定义 Hook
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
使用 Context
function App() {
return (
<ThemeProvider>
<Toolbar />
<Content />
</ThemeProvider>
);
}
function Toolbar() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
切换到{theme === 'light' ? '深色' : '浅色'}模式
</button>
);
}
useReducer Hook
用于管理复杂的状态逻辑。
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>
+1
</button>
<button onClick={() => dispatch({ type: 'decrement' })}>
-1
</button>
<button onClick={() => dispatch({ type: 'reset' })}>
重置
</button>
</div>
);
}
useMemo 和 useCallback
用于性能优化。
useMemo(缓存计算结果)
function ExpensiveComputation({ a, b }) {
const result = useMemo(() => {
console.log('执行复杂计算...');
return a * b + Math.sqrt(a + b);
}, [a, b]);
return <div>结果: {result}</div>;
}
useCallback(缓存函数)
function ParentComponent() {
const [count, setCount] = useState(0);
// 不使用 useCallback(每次渲染都创建新函数)
const handleClick = () => {
setCount(count + 1);
};
// 使用 useCallback(只在依赖变化时创建新函数)
const memoizedHandleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <ChildComponent onClick={memoizedHandleClick} />;
}
💡 最佳实践
- 依赖数组要完整:列出所有外部使用的变量
- 正确清理副作用:返回清理函数避免内存泄漏
- 合理使用 Context:只用于真正需要全局共享的状态
- 性能优化要适度:过早优化是万恶之源
🎯 练习
- 创建一个自动保存的表单组件
- 实现一个主题切换功能
- 使用 useReducer 重构一个复杂的状态逻辑
下一节我们将学习 事件处理和表单!