React Hooks 的优势和使用场景
1. Hooks 的核心优势
1.1 逻辑复用更简单
传统高阶组件(HOC)和render props会导致组件树嵌套过深,而Hooks可以通过自定义Hook实现逻辑复用:
// 自定义Hook示例
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => setSize({
width: window.innerWidth,
height: window.innerHeight
});
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 在多个组件中使用
function ComponentA() {
const size = useWindowSize();
// ...
}
function ComponentB() {
const size = useWindowSize();
// ...
}
1.2 解决class组件痛点
- 避免this绑定问题
- 生命周期逻辑分散问题
- 更好的TS类型推断
1.3 代码更简洁
相同功能下,Hooks版本通常比class组件少30%代码量:
// Class组件
class Counter extends React.Component {
state = { count: 0 };
componentDidMount() {
document.title = `Count: ${this.state.count}`;
}
componentDidUpdate() {
document.title = `Count: ${this.state.count}`;
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Count: {this.state.count}
</button>
);
}
}
// Hooks版本
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
2. 常用Hooks使用场景
2.1 useState - 状态管理
适用场景:
- 组件内部状态
- 表单控件状态
- 简单的UI状态切换
function Form() {
const [name, setName] = useState('');
const [age, setAge] = useState(18);
return (
<form>
<input value={name} onChange={e => setName(e.target.value)} />
<input
type="number"
value={age}
onChange={e => setAge(Number(e.target.value))}
/>
</form>
);
}
2.2 useEffect - 副作用处理
适用场景:
- 数据获取
- 订阅事件
- 手动DOM操作
- 定时器管理
function DataFetcher({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
const fetchData = async () => {
const result = await fetch(`/api/data/${id}`);
if (isMounted) setData(await result.json());
};
fetchData();
return () => {
isMounted = false; // 清理操作
};
}, [id]); // 依赖数组
return <div>{JSON.stringify(data)}</div>;
}
2.3 useContext - 跨组件共享状态
适用场景:
- 主题切换
- 用户认证信息
- 多语言国际化
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div style={{ background: theme === 'dark' ? '#333' : '#fff' }} />;
}
2.4 useReducer - 复杂状态逻辑
适用场景:
- 状态逻辑复杂
- 包含多个子值
- 下一个状态依赖前一个状态
function todosReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, { text: action.text, completed: false }];
case 'toggle':
return state.map((todo, i) =>
i === action.index ? {...todo, completed: !todo.completed} : todo
);
default:
return state;
}
}
function TodoList() {
const [todos, dispatch] = useReducer(todosReducer, []);
return (
<div>
{todos.map((todo, i) => (
<div key={i} onClick={() => dispatch({ type: 'toggle', index: i })}>
{todo.text}
</div>
))}
<button onClick={() => dispatch({ type: 'add', text: 'New todo' })}>
Add Todo
</button>
</div>
);
}
2.5 useMemo/useCallback - 性能优化
适用场景:
- 计算昂贵的值
- 避免不必要的子组件重渲染
- 稳定的回调引用
function ExpensiveComponent({ list, filter }) {
const filteredList = useMemo(() => {
return list.filter(item => item.includes(filter));
}, [list, filter]); // 只有依赖变化时重新计算
const handleClick = useCallback(() => {
console.log('Clicked with filter:', filter);
}, [filter]); // 保持稳定的函数引用
return (
<div>
{filteredList.map(item => (
<div key={item} onClick={handleClick}>{item}</div>
))}
</div>
);
}
3. 最佳实践
-
Hooks调用规则
- 只在React函数组件或自定义Hook中调用
- 只在最顶层调用,不能在条件、循环或嵌套函数中调用
-
依赖数组优化
- useEffect/useMemo/useCallback的依赖数组要完整
- 使用eslint-plugin-react-hooks检查依赖
-
自定义Hook封装
- 以use开头的命名约定
- 可以组合多个基础Hook
-
性能考量
- 避免在渲染函数中进行昂贵计算
- 合理使用useMemo/useCallback
- 大型列表使用虚拟滚动
-
测试策略
- 使用@testing-library/react-hooks测试自定义Hook
- 注意异步effect的测试
4. 总结
React Hooks通过函数式的方式简化了状态管理和副作用处理,主要优势包括:
- 更简洁的代码结构
- 更好的逻辑复用
- 更直观的组件编写方式
- 更友好的TypeScript支持
典型使用场景包括:
- 组件状态管理(useState)
- 副作用处理(useEffect)
- 上下文共享(useContext)
- 复杂状态逻辑(useReducer)
- 性能优化(useMemo/useCallback)
随着React 18的并发特性推出,Hooks将成为未来React开发的标准模式。建议新项目全面采用Hooks,老项目可以逐步迁移。