React 常用 Hooks 的具体代码示例,包含使用场景和实际实现
·
1. useState - 状态管理
场景:表单处理、UI 状态控制
import { useState } from 'react';
function LoginForm() {
// 初始化表单状态
const [formData, setFormData] = useState({
username: '',
password: '',
rememberMe: false
});
// 处理输入变化
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('提交表单:', formData);
// 实际登录逻辑...
};
return (
<form onSubmit={handleSubmit} className="login-form">
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
placeholder="用户名"
/>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
placeholder="密码"
/>
<label>
<input
type="checkbox"
name="rememberMe"
checked={formData.rememberMe}
onChange={handleChange}
/>
记住我
</label>
<button type="submit">登录</button>
</form>
);
}
2. useEffect - 副作用处理
场景:数据获取、事件监听
import { useState, useEffect } from 'react';
function UserActivityList({ userId }) {
const [activities, setActivities] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 获取用户活动数据
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchActivities = async () => {
try {
setLoading(true);
const response = await fetch(
`/api/users/${userId}/activities`,
{ signal }
);
if (!response.ok) throw new Error('数据获取失败');
const data = await response.json();
setActivities(data);
setError(null);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
};
fetchActivities();
// 清理函数:取消请求和事件监听
return () => {
controller.abort();
};
}, [userId]); // 当userId变化时重新获取数据
if (loading) return <div>加载中...</div>;
if (error) return <div>错误: {error}</div>;
return (
<ul className="activity-list">
{activities.map(activity => (
<li key={activity.id}>
{activity.action} - {new Date(activity.time).toLocaleString()}
</li>
))}
</ul>
);
}
3. useContext - 跨组件状态共享
场景:主题切换、用户认证状态
import { createContext, useContext, useState } from 'react';
// 创建上下文
const AuthContext = createContext();
// 提供器组件
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = (userData) => {
setUser(userData);
localStorage.setItem('user', JSON.stringify(userData));
};
const logout = () => {
setUser(null);
localStorage.removeItem('user');
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
// 自定义Hook简化使用
export function useAuth() {
return useContext(AuthContext);
}
// 使用示例 - 登录按钮
function LoginButton() {
const { user, login, logout } = useAuth();
if (user) {
return (
<div>
欢迎, {user.name}
<button onClick={logout}>退出登录</button>
</div>
);
}
return (
<button onClick={() => login({ id: 1, name: '用户' })}>
登录
</button>
);
}
4. useReducer - 复杂状态管理
场景:购物车、多步骤表单
import { useReducer } from 'react';
// 初始状态
const initialState = {
items: [],
total: 0
};
// reducer函数
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM': {
const existingItem = state.items.find(
item => item.id === action.payload.id
);
let newItems;
if (existingItem) {
newItems = state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: item.quantity + 1 }
: item
);
} else {
newItems = [...state.items, { ...action.payload, quantity: 1 }];
}
const total = newItems.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return { items: newItems, total };
}
case 'REMOVE_ITEM': {
const newItems = state.items.filter(item => item.id !== action.payload);
const total = newItems.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
return { items: newItems, total };
}
case 'CLEAR_CART':
return initialState;
default:
return state;
}
}
// 购物车组件
function ShoppingCart() {
const [state, dispatch] = useReducer(cartReducer, initialState);
return (
<div className="cart">
<h2>购物车</h2>
<button onClick={() => dispatch({
type: 'ADD_ITEM',
payload: { id: 1, name: '商品A', price: 99 }
})}>
添加商品A
</button>
<ul>
{state.items.map(item => (
<li key={item.id}>
{item.name} × {item.quantity} - ¥{item.price * item.quantity}
<button onClick={() => dispatch({
type: 'REMOVE_ITEM',
payload: item.id
})}>
移除
</button>
</li>
))}
</ul>
<p>总计: ¥{state.total}</p>
<button onClick={() => dispatch({ type: 'CLEAR_CART' })}>
清空购物车
</button>
</div>
);
}
5. useRef - DOM 访问与状态存储
场景:输入框焦点、定时器管理
import { useRef, useState, useEffect } from 'react';
function Timer() {
const [time, setTime] = useState(0);
const timerRef = useRef(null); // 存储定时器ID
const lastTimeRef = useRef(0); // 存储上一次时间
// 开始计时
const startTimer = () => {
if (!timerRef.current) {
lastTimeRef.current = Date.now() - time * 1000;
timerRef.current = setInterval(() => {
const now = Date.now();
setTime(Math.floor((now - lastTimeRef.current) / 1000));
}, 1000);
}
};
// 停止计时
const stopTimer = () => {
clearInterval(timerRef.current);
timerRef.current = null;
};
// 重置计时
const resetTimer = () => {
stopTimer();
setTime(0);
};
// 组件卸载时清理定时器
useEffect(() => {
return () => clearInterval(timerRef.current);
}, []);
return (
<div className="timer">
<h2>计时器: {time}秒</h2>
<button onClick={startTimer}>开始</button>
<button onClick={stopTimer}>停止</button>
<button onClick={resetTimer}>重置</button>
</div>
);
}
6. useCallback 与 useMemo - 性能优化
场景:优化子组件渲染、避免重复计算
import { useState, useCallback, useMemo } from 'react';
import { memo } from 'react';
// 子组件 - 使用memo避免不必要的渲染
const ExpensiveComponent = memo(({ onCalculate, items }) => {
console.log('ExpensiveComponent 渲染了');
return (
<div>
<h3>商品列表</h3>
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<button onClick={onCalculate}>计算总价</button>
</div>
);
});
function ParentComponent() {
const [count, setCount] = useState(0);
const [products, setProducts] = useState([
{ id: 1, name: '商品1', price: 100 },
{ id: 2, name: '商品2', price: 200 }
]);
// 使用useCallback记忆化函数
const calculateTotal = useCallback(() => {
console.log('计算总价...');
return products.reduce((sum, product) => sum + product.price, 0);
}, [products]); // 只有products变化时才重新创建函数
// 使用useMemo记忆化计算结果
const filteredProducts = useMemo(() => {
console.log('过滤商品...');
return products.filter(product => product.price > 150);
}, [products]); // 只有products变化时才重新计算
return (
<div>
<p>计数器: {count}</p>
<button onClick={() => setCount(count + 1)}>增加计数</button>
{/* 传递记忆化的函数和值 */}
<ExpensiveComponent
onCalculate={calculateTotal}
items={filteredProducts}
/>
</div>
);
}
除了前面介绍的常用 Hooks,React 还提供了一些其他实用的 Hooks,以及社区中常用的自定义 Hooks 模式。以下是一些补充的 Hooks 及其用法示例:
7. useLayoutEffect - 同步 DOM 操作
特点:与 useEffect 类似,但会在 DOM 更新后同步执行,适合需要立即读取布局的场景。
适用场景:测量 DOM 尺寸、避免布局抖动
import { useState, useRef, useLayoutEffect } from 'react';
function Tooltip() {
const [tooltipStyle, setTooltipStyle] = useState({ opacity: 0 });
const targetRef = useRef(null);
const tooltipRef = useRef(null);
useLayoutEffect(() => {
if (!targetRef.current || !tooltipRef.current) return;
// 获取目标元素位置信息(DOM 已更新)
const targetRect = targetRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
// 计算tooltip位置(确保不超出视口)
const left = targetRect.left + window.scrollX;
const top = targetRect.top + window.scrollY - tooltipRect.height - 10;
setTooltipStyle({
opacity: 1,
left: `${left}px`,
top: `${top}px`,
position: 'absolute'
});
}, []);
return (
<div style={{ position: 'relative' }}>
<button ref={targetRef}>Hover me</button>
<div ref={tooltipRef} style={tooltipStyle}>
This is a tooltip
</div>
</div>
);
}
8. useImperativeHandle - 自定义暴露接口
特点:自定义子组件通过 ref 暴露给父组件的方法,控制暴露的接口。
适用场景:封装组件时限制外部访问范围
import { useRef, useImperativeHandle, forwardRef } from 'react';
// 子组件 - 使用forwardRef接收ref
const TextEditor = forwardRef((props, ref) => {
const textareaRef = useRef(null);
// 自定义暴露给父组件的方法
useImperativeHandle(ref, () => ({
// 只暴露需要的方法
focus: () => {
textareaRef.current.focus();
},
getValue: () => {
return textareaRef.current.value;
}
// 不暴露内部的textareaRef
}));
return <textarea ref={textareaRef} {...props} />;
});
// 父组件使用
function EditorContainer() {
const editorRef = useRef(null);
return (
<div>
<TextEditor ref={editorRef} />
<button onClick={() => editorRef.current.focus()}>
聚焦编辑器
</button>
<button onClick={() => {
console.log('内容:', editorRef.current.getValue());
}}>
获取内容
</button>
</div>
);
}
9. useDebugValue - 调试自定义 Hook
特点:在 React DevTools 中为自定义 Hook 显示标签,提高调试体验。
适用场景:开发共享的自定义 Hook 时
import { useState, useEffect, useDebugValue } from 'react';
// 自定义Hook - 处理数据获取
function useDataFetching(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
// 在DevTools中显示状态和URL
useDebugValue(loading ? 'Loading...' : `Fetched: ${url}`);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);
return { data, loading };
}
// 使用自定义Hook
function UserList() {
// 在React DevTools中可以看到useDataFetching的调试信息
const { data, loading } = useDataFetching('/api/users');
if (loading) return <div>Loading...</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
10. 自定义 Hook - 封装复用逻辑
特点:将组件逻辑提取到可重用的函数中,是 Hooks 最强大的模式之一。
适用场景:表单处理、数据订阅、权限控制等可复用逻辑
示例 1: 表单处理自定义 Hook
import { useState } from 'react';
// 自定义表单处理Hook
function useForm(initialValues, validate) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [touched, setTouched] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
};
const handleBlur = (e) => {
const { name } = e.target;
setTouched(prev => ({ ...prev, [name]: true }));
// 验证当前字段
if (validate) {
const fieldErrors = validate(values);
setErrors(prev => ({ ...prev, [name]: fieldErrors[name] }));
}
};
const handleSubmit = (callback) => (e) => {
e.preventDefault();
// 标记所有字段为已触碰
setTouched(Object.keys(values).reduce((acc, key) => {
acc[key] = true;
return acc;
}, {}));
// 完整验证
const formErrors = validate ? validate(values) : {};
setErrors(formErrors);
// 如果没有错误,执行回调
if (Object.keys(formErrors).length === 0) {
callback(values);
}
};
return {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
};
}
// 使用示例
function RegistrationForm() {
const {
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit
} = useForm(
{ email: '', password: '' },
(values) => {
const errors = {};
if (!values.email) errors.email = '邮箱不能为空';
else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = '邮箱格式不正确';
}
if (!values.password) errors.password = '密码不能为空';
else if (values.password.length < 6) {
errors.password = '密码长度不能少于6位';
}
return errors;
}
);
const onSubmit = (data) => {
console.log('提交:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input
type="email"
name="email"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
placeholder="邮箱"
/>
{touched.email && errors.email && (
<span style={{ color: 'red' }}>{errors.email}</span>
)}
</div>
<div>
<input
type="password"
name="password"
value={values.password}
onChange={handleChange}
onBlur={handleBlur}
placeholder="密码"
/>
{touched.password && errors.password && (
<span style={{ color: 'red' }}>{errors.password}</span>
)}
</div>
<button type="submit">注册</button>
</form>
);
}
示例 2: 窗口尺寸监听 Hook
import { useState, useEffect } from 'react';
// 监听窗口尺寸的自定义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 ResponsiveComponent() {
const { width, height } = useWindowSize();
const isMobile = width < 768;
return (
<div>
<p>窗口尺寸: {width}x{height}</p>
{isMobile ? (
<p>移动端视图</p>
) : (
<p>桌面端视图</p>
)}
</div>
);
}
更多推荐



所有评论(0)