React Hooks 的优势和使用场景
1. Hooks 的核心优势
1.1 逻辑复用更简单
Hooks 解决了高阶组件和 render props 带来的"嵌套地狱"问题。通过自定义 Hook 可以轻松复用状态逻辑,而不需要改变组件结构。
// 自定义计数器 Hook
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
// 在组件中使用
function ComponentA() {
const { count, increment } = useCounter(0);
return <button onClick={increment}>{count}</button>;
}
1.2 代码更简洁
相比 class 组件,函数组件 + Hooks 的代码量通常减少 30%-50%,且更易理解。
1.3 更好的关注点分离
相关逻辑可以组织在一起,而不是像 class 组件那样分散在不同的生命周期方法中。
2. 常用 Hooks 及使用场景
2.1 useState - 状态管理
适用于组件内部的状态管理,替代 this.state。
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
2.2 useEffect - 副作用处理
替代 componentDidMount、componentDidUpdate 和 componentWillUnmount 的组合。
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`/api/users/${userId}`);
setUser(await response.json());
};
fetchData();
return () => {
// 清理函数,相当于 componentWillUnmount
};
}, [userId]); // 仅在 userId 变化时重新执行
}
2.3 useContext - 跨组件数据共享
简化 context 的使用,避免 Consumer 的嵌套。
const ThemeContext = 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 - 复杂状态逻辑
适合管理包含多个子值的 state 对象,或下一个 state 依赖于前一个 state 的情况。
function todosReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, { text: action.text, completed: false }];
case 'toggle':
return state.map((todo, index) =>
index === action.index ? {...todo, completed: !todo.completed} : todo
);
default:
return state;
}
}
function TodoList() {
const [todos, dispatch] = useReducer(todosReducer, []);
function handleAdd(text) {
dispatch({ type: 'add', text });
}
}
3. 高级使用场景
3.1 自定义 Hooks
封装可复用的业务逻辑,如数据获取、表单处理等。
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
};
fetchData();
}, [url]);
return { data, loading };
}
// 使用示例
function UserInfo({ userId }) {
const { data: user, loading } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
3.2 性能优化
使用 useMemo 和 useCallback 避免不必要的重新计算和渲染。
function ExpensiveComponent({ list }) {
const sortedList = useMemo(() => {
return list.sort((a, b) => a.value - b.value);
}, [list]);
const handleClick = useCallback(() => {
console.log('Item clicked');
}, []);
return (
<ul>
{sortedList.map(item => (
<li key={item.id} onClick={handleClick}>{item.name}</li>
))}
</ul>
);
}
4. 最佳实践
- 只在顶层调用 Hooks:不要在循环、条件或嵌套函数中调用 Hook
- 命名约定:自定义 Hook 名称应以 "use" 开头
- 依赖数组:正确设置 useEffect 和 useMemo 的依赖数组
- 拆分复杂组件:当组件变得复杂时,考虑拆分成多个小组件或自定义 Hook
5. 何时使用 Hooks
- 新项目开发首选 Hooks
- 重构旧组件时逐步引入
- 需要逻辑复用的场景
- 需要更简洁代码的项目
Hooks 代表了 React 的未来发展方向,它们简化了 React 组件的开发模式,使代码更易于维护和理解。随着 React 18 的发布,Hooks 的性能和功能还在不断增强。