【前端笔记】React常用内置hook总结
1.React常用内置hook
1.1 useState — 组件状态
作用:在函数组件里保存可变状态,更新后触发重新渲染。
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(prev => prev - 1)}>-1</button>
</div>
);
}
要点:
- 初始值可以是
useState(() => expensiveInit())惰性初始化 - 对象/数组更新要创建新引用:
setUser({ ...user, name: 'Tom' })
1.2 useEffect — 副作用
作用:在渲染后执行副作用(请求数据、订阅、操作 DOM 等)。
import { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
if (!cancelled){
setUser(data);
}
}
);
// 清理函数:组件卸载或依赖变化前执行
return () => {
cancelled = true;
};
}, [userId]); // 依赖数组:userId 变化时重新执行
if (!user) return (
<p>加载中...</p>
);
return (
<h1>{user.name}</h1>
);
}
依赖数组规则:
| 写法 | 行为 |
|---|---|
|
无依赖数组 |
每次渲染后都执行 |
|
|
仅挂载时执行一次 |
|
|
|
1.3 useContext — 跨层级共享数据
作用:读取 Context,避免逐层 props 传递。
import { createContext, useContext, useState } from 'react';
const ThemeContext = createContext('light');
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
当前主题: {theme}
</button>
);
}
1.4 useRef — 持久引用 / DOM 引用
作用:保存跨渲染不变的可变值;或获取 DOM 节点。改 .current 不会触发重渲染。
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
inputRef.current?.focus();
});
return (
<>
<input ref={inputRef} />
<p>渲染次数: {renderCount.current}</p>
</>
);
}
1.5 useReducer — 复杂状态逻辑
作用:用 dispatch(action) 管理复杂状态,类似 Redux 思路。
import { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'decrement': return { count: state.count - 1 };
case 'reset': return initialState;
default: return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}
适合:多字段表单、状态机、下一步依赖上一步的更新。
1.6 useMemo — 缓存计算结果
作用:依赖不变时复用上次计算结果,减少昂贵运算。
import { useMemo, useState } from 'react';
function ProductList({ products, filter }) {
const [sortOrder, setSortOrder] = useState('asc');
const filtered = useMemo(() => {
console.log('重新过滤...');
return products
.filter(p => p.name.includes(filter))
.sort((a, b) => sortOrder === 'asc' ? a.price - b.price : b.price - a.price);
}, [products, filter, sortOrder]);
return (
<ul>
{filtered.map(p => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
1.7 useCallback — 缓存函数引用
作用:依赖不变时返回同一函数引用,常用于传给子组件或作为 useEffect 依赖。
import { useState, useCallback, memo } from 'react';
const Child = memo(function Child({ onClick }) {
console.log('Child 渲染');
return <button onClick={onClick}>点击</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // 不依赖外部变量,引用稳定
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<Child onClick={handleClick} />
<p>{count}</p>
</>
);
}
注意:不要滥用;只有子组件用了 memo 或函数作为依赖时才明显有用。
1.8.useLayoutEffect — 同步副作用
作用:与 useEffect 类似,但在浏览器绘制之前同步执行。适合测量 DOM、同步改样式,避免闪烁。
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip({ children }) {
const ref = useRef(null);
const [top, setTop] = useState(0);
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setTop(rect.bottom + 8); // 在绘制前算好位置
}, []);
return (
<div ref={ref}>
{children}
<div style={{ position: 'absolute', top }}>提示内容</div>
</div>
);
}
1.9 useId — 生成唯一 ID
作用:生成 SSR 安全的稳定 ID,用于 label / aria 关联。
import { useId } from 'react';
function EmailField() {
const id = useId();
return (
<>
<label htmlFor={id}>邮箱</label>
<input id={id} type="email" aria-describedby={`${id}-hint`} />
<span id={`${id}-hint`}>我们不会泄露你的邮箱</span>
</>
);
}
1.10 useImperativeHandle — 自定义暴露给父组件的 ref
作用:配合 forwardRef,只暴露部分方法给父组件。
import { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef(function FancyInput(props, ref) {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => { inputRef.current.value = ''; },
}));
return <input ref={inputRef} {...props} />;
});
// 父组件
function Form() {
const inputRef = useRef(null);
return (
<>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current?.focus()}>聚焦</button>
</>
);
}
1.11 useTransition — 非紧急更新
作用:把状态更新标为非紧急,保持 UI 响应(如大列表筛选)。
import { useState, useTransition } from 'react';
function SearchList({ items }) {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
const value = e.target.value;
setQuery(value); // 紧急:输入框立刻更新
startTransition(() => {
// 非紧急:列表过滤可以稍后
setFiltered(items.filter(i => i.includes(value)));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <span>更新中...</span>}
</>
);
}
1.12 useDeferredValue — 延迟显示值
作用:延迟某值的更新,让更紧急的 UI 先渲染。
import { useState, useDeferredValue, memo } from 'react';
const SlowList = memo(function SlowList({ text }) {
// 假设渲染很慢
return <ul>{/* 大量项 */}</ul>;
});
function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
<input value={text} onChange={e => setText(e.target.value)} />
<SlowList text={deferredText} />
</>
);
}
1.13 useSyncExternalStore — 订阅外部数据源
作用:安全订阅浏览器 API 或外部 store(如 Redux)。
import { useSyncExternalStore } from 'react';
function useMediaQuery(query) {
return useSyncExternalStore(
(callback) => {
const mq = window.matchMedia(query);
mq.addEventListener('change', callback);
return () => mq.removeEventListener('change', callback);
},
() => window.matchMedia(query).matches,
() => false // SSR 回退值
);
}
function Responsive() {
const isMobile = useMediaQuery('(max-width: 768px)');
return <p>{isMobile ? '移动端' : '桌面端'}</p>;
}
2. React 19 新增 Hook
2.1 useActionState(原 useFormState)
处理表单 action 的状态(pending、返回值等)。
import { useActionState } from 'react';
async function submitForm(prevState, formData) {
const name = formData.get('name');
if (!name) return { error: '姓名不能为空' };
await saveUser(name);
return { success: true };
}
function MyForm() {
const [state, formAction, isPending] = useActionState(submitForm, null);
return (
<form action={formAction}>
<input name="name" />
<button disabled={isPending}>{isPending ? '提交中...' : '提交'}</button>
{state?.error && <p>{state.error}</p>}
</form>
);
}
2.2 useOptimistic — 乐观更新
import { useOptimistic, useState } from 'react';
function Messages({ messages, sendMessage }) {
const [optimisticMessages, addOptimistic] = useOptimistic(
messages,
(state, newMsg) => [...state, { ...newMsg, sending: true }]
);
async function handleSend(text) {
addOptimistic({ id: Date.now(), text });
await sendMessage(text);
}
return (
<ul>
{optimisticMessages.map(m => (
<li key={m.id}>{m.text} {m.sending && '(发送中)'}</li>
))}
</ul>
);
}
2.3 use() — 读取 Promise 或 Context
import { use, Suspense } from 'react';
function User({ userPromise }) {
const user = use(userPromise); // 需在 Suspense 边界内
return <p>{user.name}</p>;
}
function App() {
const userPromise = fetchUser(1);
return (
<Suspense fallback={<p>加载中...</p>}>
<User userPromise={userPromise} />
</Suspense>
);
}
3. 总结和建议
3.1 快速对照表
| Hook | 一句话 |
|---|---|
|
|
组件本地状态 |
|
|
渲染后的副作用 |
|
|
读 Context |
|
|
持久引用 / DOM |
|
|
复杂状态 + dispatch |
|
|
缓存计算结果 |
|
|
缓存函数引用 |
|
|
绘制前的同步副作用 |
|
|
唯一 ID(SSR 安全) |
|
|
自定义 ref 暴露 |
|
|
标记非紧急更新 |
|
|
延迟某值更新 |
|
|
订阅外部 store |
|
|
表单 action 状态 |
|
|
乐观 UI 更新 |
|
|
读 Promise / Context |
3.2 使用建议
- 默认用
useState+useEffect,复杂了再考虑useReducer。 useMemo/useCallback先别过早优化,有性能问题再加。useEffect依赖要写全,避免闭包旧值和无限循环。- 数据请求优先考虑 React Query / SWR,或 React 19 的
use+ Server Components。 - 自定义 Hook 是把逻辑复用的最佳方式:把
useState+useEffect等组合封装成useXxx()。
更多推荐
所有评论(0)