React开发中除useState外实用Hook推荐及useEffect用法解惑
React实用Hook详解(除useState外)
useEffect:核心用法与适用场景
useEffect是处理副作用的核心Hook,副作用指组件渲染完成后需要执行的操作:数据请求、DOM操作、订阅/取消订阅、定时器等。
使用时机与方法
- 组件首次挂载后执行:给依赖数组传空数组
[],只会在组件挂载时运行一次useEffect(() => { // 初始化请求数据 const fetchUser = async () => { const res = await fetch('/api/user'); const user = await res.json(); setUser(user); }; fetchUser(); }, []); - 依赖项变化时执行:依赖数组中指定变量,当这些变量更新时重新运行副作用
useEffect(() => { // 当userId变化时,重新请求用户数据 const fetchUser = async () => { const res = await fetch(`/api/user/${userId}`); const user = await res.json(); setUser(user); }; fetchUser(); }, [userId]); // 依赖userId,变化时触发 - 组件每次渲染都执行:不传递依赖数组,会在组件每次渲染(包括挂载、更新)时都运行,除非必要否则避免使用
- 组件卸载/依赖变化前清理:返回一个清理函数,用于取消订阅、清除定时器、取消网络请求等,防止内存泄漏
useEffect(() => { const socket = io.connect('/'); socket.on('message', setMessage); // 组件卸载或依赖变化时关闭socket连接 return () => { socket.off('message', setMessage); socket.disconnect(); }; }, []);
其他高频实用Hook
useContext
- 适用场景:跨多层组件共享状态(如主题、用户登录态),避免props逐层传递的繁琐
- 示例:
// 全局创建Context const UserContext = React.createContext(null); // 父组件提供Context值 function App() { const [user, setUser] = useState({ name: 'Alice', id: 1 }); return ( <UserContext.Provider value={user}> <Navbar /> <Profile /> </UserContext.Provider> ); } // 子组件直接获取Context值 function Profile() { const user = useContext(UserContext); return <div>当前用户:{user.name}</div>; }
useReducer
- 适用场景:处理复杂状态逻辑,比如状态包含多个子属性、状态更新依赖复杂计算、多个状态操作关联时,替代多个
useState让代码更清晰 - 示例:
const initialState = { count: 0, step: 1 }; function countReducer(state, action) { switch (action.type) { case 'increment': return { ...state, count: state.count + state.step }; case 'decrement': return { ...state, count: state.count - state.step }; case 'setStep': return { ...state, step: action.payload }; default: throw new Error('未知操作类型'); } } function Counter() { const [state, dispatch] = useReducer(countReducer, initialState); return ( <div> <p>计数:{state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> <input type="number" value={state.step} onChange={(e) => dispatch({ type: 'setStep', payload: Number(e.target.value) })} /> </div> ); }
useCallback
- 适用场景:缓存函数引用,配合
React.memo优化子组件渲染——避免因父组件重新渲染导致函数重新创建,进而触发子组件不必要的重渲染 - 示例:
function Parent() { const [count, setCount] = useState(0); // 缓存handleClick函数,只有count变化时才重新生成 const handleClick = useCallback(() => { setCount(prev => prev + 1); }, [count]); // 子组件用React.memo包裹,只有props变化时才渲染 return <Child onClick={handleClick} />; } const Child = React.memo(({ onClick }) => { console.log('子组件渲染'); return <button onClick={onClick}>点击计数</button>; });
useMemo
- 适用场景:缓存昂贵计算的结果,避免组件每次渲染都重复执行高开销计算(比如大数据量处理、复杂数学运算)
- 示例:
// 模拟昂贵计算 const calculateTotal = (items) => { console.log('执行计算...'); return items.reduce((sum, item) => sum + item.price, 0); }; function Cart({ items }) { // 只有items变化时才重新计算总价 const total = useMemo(() => calculateTotal(items), [items]); return <div>购物车总价:¥{total}</div>; }
useRef
- 适用场景:
- 获取DOM元素的引用,用于操作DOM(比如聚焦输入框、获取元素尺寸)
- 保存跨渲染周期的可变值,修改它不会触发组件重渲染
- 示例:
// 获取DOM元素 function InputAutoFocus() { const inputRef = useRef(null); useEffect(() => { inputRef.current.focus(); }, []); return <input ref={inputRef} type="text" />; } // 保存跨渲染的值 function Counter() { const [count, setCount] = useState(0); const prevCount = useRef(); useEffect(() => { prevCount.current = count; }, [count]); return ( <div> 当前计数:{count},上一次计数:{prevCount.current} <button onClick={() => setCount(prev => prev + 1)}>+1</button> </div> ); }
useLayoutEffect
- 适用场景:需要在DOM更新完成后、浏览器绘制之前同步执行的操作,比如获取DOM布局信息并立即更新状态,避免页面出现视觉闪烁(和
useEffect的区别是它在浏览器绘制前执行,同步阻塞渲染) - 示例:
function ElementSize() { const [height, setHeight] = useState(0); const divRef = useRef(null); useLayoutEffect(() => { // DOM更新后立即获取高度,同步设置状态 setHeight(divRef.current.offsetHeight); }, []); return <div ref={divRef}>元素高度:{height}px</div>; }
内容的提问来源于stack exchange,提问作者aqtar parveez
相关产品推荐
相关产品推荐

