React Hooks 的优势和使用场景
1. React Hooks 的核心优势
1.1 逻辑复用更便捷
- 传统高阶组件(HOC)和render props模式会导致组件嵌套过深
- 自定义Hook可以轻松提取和复用状态逻辑
- 示例:多个组件共享鉴权逻辑
// 自定义Hook示例
function useAuth() {
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setUser(user);
});
return unsubscribe;
}, []);
return user;
}
// 组件中使用
function Profile() {
const user = useAuth();
// ...
}
1.2 简化组件代码
- 消除类组件的样板代码(constructor, this绑定等)
- 相关逻辑可以组织在一起而非分散在不同生命周期
- 示例:计数器组件对比
// 类组件
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.increment}>{this.state.count}</button>;
}
}
// 函数组件+Hooks
function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return <button onClick={increment}>{count}</button>;
}
1.3 更细粒度的控制
- useEffect可以针对不同副作用分别管理
- 避免类组件中生命周期方法的耦合
- 示例:数据获取与事件监听分离
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
// 数据获取
useEffect(() => {
fetchUser(userId).then(setUser);
}, [userId]);
// 窗口事件监听
useEffect(() => {
const handleResize = () => console.log(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
// ...
}
2. 核心Hooks使用场景
2.1 useState - 状态管理
- 适合组件内部简单状态
- 替代this.state的解决方案
- 示例:表单控制
function Form() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = e => {
e.preventDefault();
console.log({ name, email });
};
return (
<form onSubmit={handleSubmit}>
<input value={name} onChange={e => setName(e.target.value)} />
<input value={email} onChange={e => setEmail(e.target.value)} />
<button type="submit">Submit</button>
</form>
);
}
2.2 useEffect - 副作用处理
- 数据获取、订阅、手动DOM操作
- 替代componentDidMount/Update/WillUnmount
- 关键点:依赖数组控制执行时机
function DataFetcher({ url }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let isMounted = true;
const fetchData = async () => {
try {
const response = await fetch(url);
if (isMounted && response.ok) {
setData(await response.json());
setLoading(false);
}
} catch (error) {
if (isMounted) setLoading(false);
}
};
fetchData();
return () => {
isMounted = false; // 清理操作
};
}, [url]); // url变化时重新获取
if (loading) return <div>Loading...</div>;
return <div>{JSON.stringify(data)}</div>;
}
2.3 useContext - 跨组件状态共享
- 替代Context.Consumer的嵌套写法
- 适合主题、用户信息等全局状态
- 示例:主题切换
const ThemeContext = React.createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<Toolbar />
<button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
function Toolbar() {
return <ThemedButton />;
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{
background: theme === 'dark' ? '#333' : '#EEE',
color: theme === 'dark' ? '#FFF' : '#000'
}}>Themed Button</button>;
}
2.4 useReducer - 复杂状态逻辑
- 适合状态更新逻辑复杂的场景
- 替代Redux的轻量级方案
- 示例:购物车管理
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return [...state, action.item];
case 'REMOVE_ITEM':
return state.filter(item => item.id !== action.id);
case 'CLEAR_CART':
return [];
default:
return state;
}
}
function ShoppingCart() {
const [cart, dispatch] = useReducer(cartReducer, []);
return (
<div>
<button onClick={() => dispatch({
type: 'ADD_ITEM',
item: { id: Date.now(), name: 'New Item' }
})}>
Add Item
</button>
<ul>
{cart.map(item => (
<li key={item.id}>
{item.name}
<button onClick={() => dispatch({
type: 'REMOVE_ITEM',
id: item.id
})}>
Remove
</button>
</li>
))}
</ul>
</div>
);
}
2.5 useMemo/useCallback - 性能优化
- useMemo: 缓存计算结果
- useCallback: 缓存函数引用
- 避免不必要的重新计算和渲染
function ExpensiveComponent({ list, filterText }) {
const filteredList = useMemo(() => {
console.log('Filtering...');
return list.filter(item =>
item.name.includes(filterText)
);
}, [list, filterText]); // 只有依赖变化时才重新计算
const handleClick = useCallback(() => {
console.log('Item clicked');
}, []); // 空依赖表示函数不会改变
return (
<ul>
{filteredList.map(item => (
<li key={item.id} onClick={handleClick}>
{item.name}
</li>
))}
</ul>
);
}
3. 最佳实践建议
-
Hook使用规则
- 只在顶层调用Hook
- 只在React函数中调用Hook
-
自定义Hook规范
- 命名以use开头
- 可以调用其他Hook
- 明确输入输出
-
性能优化时机
- 使用useMemo/useCallback避免不必要的计算
- 拆分大组件为小组件利用React.memo
-
副作用管理
- 每个useEffect只做一件事
- 不要忘记清理操作
- 正确处理依赖数组
-
状态设计原则
- 相关状态尽量合并
- 复杂状态考虑useReducer
- 避免深层嵌套状态
React Hooks通过提供更直接的API来使用React特性,显著简化了组件开发模式。合理运用各种Hook可以构建出更清晰、更易维护的React应用。