React Hooks 的优势和使用场景
核心优势
- 简化组件逻辑
Hooks 让函数组件也能拥有状态和生命周期,避免了 class 组件的复杂结构:
// 传统 class 组件
class Example extends React.Component {
state = { count: 0 };
componentDidMount() {
document.title = `点击了 ${this.state.count} 次`;
}
componentDidUpdate() {
document.title = `点击了 ${this.state.count} 次`;
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
点击 {this.state.count} 次
</button>
);
}
}
// 使用 Hooks 的函数组件
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `点击了 ${count} 次`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
点击 {count} 次
</button>
);
}
- 逻辑复用能力
自定义 Hook 可以提取组件逻辑,实现跨组件复用:
// 自定义 Hook
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return width;
}
// 在多个组件中使用
function ComponentA() {
const width = useWindowWidth();
// ...
}
function ComponentB() {
const width = useWindowWidth();
// ...
}
主要使用场景
- 状态管理
useState适用于组件内部状态管理:
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>
);
}
- 副作用处理
useEffect处理数据获取、订阅等副作用:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let isMounted = true;
fetchUser(userId).then(data => {
if (isMounted) setUser(data);
});
return () => {
isMounted = false; // 清理操作
};
}, [userId]); // 依赖项变化时重新执行
// ...
}
- 性能优化
useMemo和useCallback避免不必要的计算和渲染:
function ExpensiveComponent({ list }) {
const sortedList = useMemo(() => {
return list.sort((a, b) => a.value - b.value);
}, [list]); // 只有 list 变化时重新计算
const handleClick = useCallback(() => {
console.log('Clicked:', sortedList);
}, [sortedList]); // 保持函数引用稳定
// ...
}
- 复杂状态逻辑
useReducer适合管理包含多个子值的复杂 state:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer, []);
function handleAddTodo(text) {
dispatch({ type: 'ADD', text });
}
// ...
}
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;
}
}
最佳实践
-
Hook 使用规则
- 只在 React 函数组件或自定义 Hook 中调用 Hook
- 只在最顶层调用 Hook,不要在循环、条件或嵌套函数中调用
-
依赖项数组优化
useEffect的依赖项要包含所有外部值- 使用
useCallback和useMemo避免不必要的重新创建
-
自定义 Hook 封装
- 将通用逻辑提取为自定义 Hook
- 命名以
use开头便于识别
-
性能考虑
- 避免在渲染函数中进行昂贵计算
- 使用 React DevTools 分析组件更新
常见问题解决方案
- 无限循环问题
当useEffect依赖的状态在 effect 内部被更新时会导致无限循环:
// 错误示例
useEffect(() => {
setCount(count + 1); // 会导致无限循环
}, [count]);
// 正确解决方案
useEffect(() => {
setCount(prev => prev + 1); // 使用函数式更新
}, []); // 空依赖数组
- 过时闭包问题
定时器或事件监听器可能捕获过时的状态:
// 错误示例
useEffect(() => {
const id = setInterval(() => {
console.log(count); // 总是初始值
}, 1000);
return () => clearInterval(id);
}, []);
// 正确解决方案
useEffect(() => {
const id = setInterval(() => {
console.log(count); // 最新值
}, 1000);
return () => clearInterval(id);
}, [count]); // 添加依赖
- 条件执行 Hook
通过提前返回来实现条件逻辑:
function MyComponent({ shouldFetch }) {
if (!shouldFetch) return null;
// 只有在 shouldFetch 为 true 时才执行 Hook
const data = useFetchData();
return <div>{data}</div>;
}
总结
React Hooks 通过以下方式改变了 React 开发模式:
- 使函数组件具备完整能力
- 通过自定义 Hook 实现逻辑复用
- 减少组件嵌套(render props/HOC)
- 提供更细粒度的代码组织方式
- 简化测试(纯函数组件)
正确使用 Hooks 可以:
- 减少约 30% 的代码量
- 提高组件可维护性
- 优化渲染性能
- 改善代码可读性
随着 React 18 并发特性的推出,Hooks 将成为未来 React 开发的标准模式。