Hooks
1. useState () 基本用法 相关问题 - 为什么不直接用变量而用 useState ()? - 修改变量本身不会让 React 框架感知 - setState(C+1) 为什么不会立即修改
技术 |
2026/7/22
1. useState ()
基本用法
const [state, setState] = useState(initialState);
相关问题
- 为什么不直接用变量而用 useState ()?
- 修改变量本身不会让 React 框架感知
- setState(C+1) 为什么不会立即修改?
- setState 结束后进入更新队列,函数结束后才进行更新(批量更新减少重复渲染)
- 正确使用:
setState (c=>c+1)函数式更新 - React 把每一次渲染(render)都看成一个不可变的快照(snapshot)。
2. useEffect ()
基本用法
useEffect(() => {
// 与外部系统同步
return () => {
// 清理副作用
};
}, [dependencies]);
相关问题
- useEffect 的执行时机是什么?
- React 提交 DOM 更新后执行,通常不会阻塞浏览器绘制
- 它适合请求数据、订阅事件、定时器等“与外部系统同步”的操作
- 依赖数组分别代表什么?
- 不传依赖数组:每次提交后执行
- 传空数组
[]:组件挂载后执行,卸载时清理 - 传
[a, b]:挂载后执行,并在依赖变化后重新执行 - React 使用
Object.is比较新旧依赖
- 清理函数什么时候执行?
- 组件卸载时执行
- Effect 重新执行前,先用旧值执行上一次的清理函数
- 为什么开发环境中 Effect 可能执行两次?
StrictMode会在开发环境额外执行一次“创建 → 清理 → 创建”,用于发现缺少清理逻辑的问题- 生产环境不会因为
StrictMode额外执行这一次
- 为什么不能随意省略依赖?
- Effect 内使用的响应式值都应声明为依赖,否则容易产生闭包读取旧值的问题
- 对象或函数每次渲染都可能是新引用,可将其移入 Effect、用
useMemo/useCallback稳定引用,或重新设计逻辑
- 可以直接把 Effect 回调写成
async吗?- 不可以。Effect 回调只能返回清理函数或不返回内容,而
async函数一定返回 Promise - 应在 Effect 内定义异步函数,并在清理函数中忽略过期结果或取消请求
- 不可以。Effect 回调只能返回清理函数或不返回内容,而
3. useLayoutEffect ()
基本用法
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setPosition(rect);
}, []);
相关问题
- useLayoutEffect 和 useEffect 有什么区别?
useLayoutEffect在 DOM 更新后、浏览器绘制前同步执行useEffect通常在浏览器绘制后执行,不阻塞页面展示
- 什么场景使用 useLayoutEffect?
- 读取布局并立即修正,例如测量元素尺寸、设置浮层位置,避免页面闪烁
- 它会阻塞绘制,能用
useEffect时应优先使用useEffect
- 服务端渲染时要注意什么?
- 服务端没有布局信息,
useLayoutEffect只会在客户端运行 - 依赖布局的组件通常应仅在客户端渲染,或改用
useEffect
- 服务端没有布局信息,
4. useRef ()
基本用法
const inputRef = useRef<HTMLInputElement | null>(null);
const countRef = useRef(0);
相关问题
- useRef 有什么作用?
- 获取 DOM 节点或组件暴露的命令式句柄
- 保存跨渲染周期不丢失、但变化时不需要触发渲染的数据
- useRef 和 useState 有什么区别?
- 修改
ref.current不会触发重新渲染 - State 用于影响 UI 的数据,Ref 用于渲染不需要展示的数据
- 修改
- 为什么不能在渲染期间随意读写 ref?
- 渲染函数应保持纯函数特性,读写 ref 会让结果依赖调用顺序,在并发渲染下可能出现不可预测行为
- 惰性初始化且结果稳定的场景可以例外处理
- useRef 能解决闭包读取旧值的问题吗?
- 可以把最新值同步到
ref.current,供异步回调读取 - 但如果该值影响页面展示,仍应使用 State,不能用 Ref 绕过渲染
- 可以把最新值同步到
5. useMemo ()
基本用法
const filteredList = useMemo(
() => filterList(list, keyword),
[list, keyword],
);
相关问题
- useMemo 的作用是什么?
- 缓存一次计算结果,依赖没有变化时复用上次结果
- 常用于昂贵计算,或为
memo子组件提供稳定的对象、数组引用
- useMemo 是否越多越好?
- 不是。它本身有依赖比较、保存结果和增加代码复杂度的成本
- 应先确认存在性能问题,再通过性能分析决定是否使用
- useMemo 能保证结果永远不被丢弃吗?
- 不能。它是性能优化手段,不是语义保证,React 在特定情况下可能丢弃缓存
- 程序不应依赖
useMemo才能保证逻辑正确
- useMemo 回调中可以执行副作用吗?
- 不可以。回调在渲染期间执行,必须保持纯函数;副作用应放在事件处理函数或 Effect 中
6. useCallback ()
基本用法
const handleSubmit = useCallback(() => {
submitForm(productId);
}, [productId]);
相关问题
- useCallback 的作用是什么?
- 缓存函数本身,在依赖不变时保持函数引用稳定
- 常与
memo配合,或用于稳定其他 Hook 的函数依赖
- useCallback 和 useMemo 有什么区别?
useCallback(fn, deps)缓存函数useMemo(() => value, deps)缓存计算结果useCallback(fn, deps)可理解为useMemo(() => fn, deps)
- 使用 useCallback 后子组件一定不会渲染吗?
- 不一定。还需要子组件使用
memo,并且其他 Props 也没有变化 - 子组件自身 State 或读取的 Context 变化时仍会渲染
- 不一定。还需要子组件使用
- 如何减少回调对 State 的依赖?
- 如果新 State 只依赖旧 State,应使用函数式更新,例如
setTodos(todos => [...todos, newTodo])
- 如果新 State 只依赖旧 State,应使用函数式更新,例如
7. useContext ()
基本用法
const theme = useContext(ThemeContext);
相关问题
- useContext 解决什么问题?
- 让组件读取上层最近的 Context Provider 提供的数据,避免逐层传递 Props
- Context 变化时哪些组件会更新?
- Provider 的
value发生变化时,读取该 Context 的消费组件会重新渲染 memo不能阻止组件因所读取的 Context 变化而重新渲染
- Provider 的
- 如何减少 Context 导致的不必要渲染?
- 避免每次渲染都创建新的
value对象,可根据实际情况使用useMemo - 将频繁变化和很少变化的数据拆分成不同 Context
- 将读取 Context 的组件下沉,并通过 Props 把必要数据传给被
memo包裹的子组件
- 避免每次渲染都创建新的
- Context 能完全替代状态管理库吗?
- 不能简单等同。Context 负责跨层级传值,本身不提供选择器、细粒度订阅、中间件等能力
8. useReducer ()
基本用法
const [state, dispatch] = useReducer(reducer, initialState);
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
default:
return state;
}
}
相关问题
- useReducer 适合什么场景?
- State 结构复杂、多个字段联动,或下一状态依赖不同类型的操作时
- 将状态变更逻辑集中到 Reducer,便于测试和维护
- useReducer 和 useState 如何选择?
- 简单、独立的状态优先使用
useState - 状态转换规则复杂或需要明确 Action 语义时使用
useReducer
- 简单、独立的状态优先使用
- Reducer 为什么必须是纯函数?
- 相同的 State 和 Action 应得到相同结果,不能请求数据、操作 DOM 或修改外部变量
- 不能直接修改原 State,应返回新对象,否则 React 可能使用
Object.is判断状态未变化并跳过更新
- dispatch 的引用会变化吗?
dispatch的引用稳定,通常可以安全省略在 Effect 依赖之外;即使写入依赖数组也不会导致 Effect 重跑
9. useImperativeHandle ()
基本用法
useImperativeHandle(ref, () => ({
focus() {
inputRef.current?.focus();
},
}), []);
相关问题
- useImperativeHandle 的作用是什么?
- 自定义父组件通过 Ref 能访问的内容,只暴露必要的命令式方法,而不是整个 DOM 节点
- 为什么不推荐大量使用?
- React 更推荐通过 Props 描述 UI;命令式调用会增加组件耦合
- 它适合聚焦、滚动、文本选择、动画等难以完全声明式表达的操作
- ref 如何传入子组件?
- React 19 中函数组件可以把
ref作为 Prop 接收 - React 18 及更早版本通常需要使用
forwardRef
- React 19 中函数组件可以把
10. useId ()
基本用法
const id = useId();
return <input id={id} aria-describedby={`${id}-hint`} />;
相关问题
- useId 的主要用途是什么?
- 生成服务端和客户端一致的唯一 ID,常用于表单标签和无障碍属性关联
- 为什么不用随机数或自增变量生成 ID?
- 服务端渲染和客户端 Hydration 可能生成不同结果,导致不匹配
- useId 可以作为列表的 key 吗?
- 不可以。列表 Key 应来自数据本身,用于标识业务数据的稳定身份
11. useTransition ()
基本用法
const [isPending, startTransition] = useTransition();
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
相关问题
- useTransition 解决什么问题?
- 把非紧急更新标记为 Transition,使输入、点击等紧急更新可以优先响应
isPending可用于展示过渡中的等待状态
- Transition 会让更新延迟固定时间吗?
- 不会。它是更新优先级调度,不是防抖或节流
- 过渡渲染可被更高优先级更新打断,然后使用最新数据重新渲染
- 为什么不能用 Transition 控制文本输入值?
- 输入框受控值需要同步响应用户输入,应保持高优先级
- 可以同步更新输入值,再用 Transition 更新依赖该值的复杂结果区域
startTransition中包含await时有什么注意点?await之后的 State 更新目前需要再次使用startTransition包裹,才能继续被标记为 Transition
12. useDeferredValue ()
基本用法
const deferredKeyword = useDeferredValue(keyword);
const result = useMemo(
() => searchList(deferredKeyword),
[deferredKeyword],
);
相关问题
- useDeferredValue 的作用是什么?
- 延迟更新某个值,让界面先响应紧急更新,再在后台渲染依赖该值的较慢部分
- useDeferredValue 和 useTransition 有什么区别?
useTransition包裹并控制产生更新的代码useDeferredValue延迟一个已经得到的值,适合无法直接控制更新来源的情况
- useDeferredValue 是防抖吗?
- 不是。它没有固定延迟,React 会根据渲染调度尽快处理后台更新
- 它不会减少网络请求,若要减少请求次数仍需防抖或缓存
- 为什么通常还要配合 memo?
- 父组件仍会因原值变化而渲染;慢子组件使用
memo后,才能在延迟值未变化时跳过渲染
- 父组件仍会因原值变化而渲染;慢子组件使用
13. useSyncExternalStore ()
基本用法
const snapshot = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getServerSnapshot,
);
相关问题
- useSyncExternalStore 用于什么场景?
- 订阅 React 外部的数据源,例如第三方状态容器、浏览器 API 或自定义 Store
- 普通业务组件通常不直接使用,更多用于状态库封装
- 为什么不直接用 useEffect 订阅外部 Store?
useSyncExternalStore能与并发渲染和服务端渲染正确配合,避免读取到前后不一致的数据
- getSnapshot 有什么要求?
- Store 未变化时必须返回缓存的同一份快照,不能每次都创建新对象
subscribe最好定义在组件外部,避免每次渲染都重新订阅
18. Hooks 通用规则与面试考点
基本规则
- 只能在 React 函数组件或自定义 Hook 中调用 Hook
- 除
use外,Hook 必须在组件顶层调用,不能放在条件、循环、嵌套函数或try...catch中 - 自定义 Hook 名称应以
use开头
相关问题
- 为什么 Hook 不能条件调用?
- React 依赖每次渲染时 Hook 的调用顺序来对应内部状态
- 条件调用会改变顺序,导致不同 Hook 读取到错误的状态
- 自定义 Hook 会共享 State 吗?
- 不会。自定义 Hook 共享的是状态逻辑,每次调用拥有独立 State
- 如果需要共享同一份状态,应将 State 提升、使用 Context 或订阅外部 Store
- Hook 中为什么容易出现闭包旧值问题?
- 每次渲染都会创建新的变量和函数,异步回调捕获的是创建它那次渲染的快照
- 可根据场景使用正确依赖、函数式更新、Ref,或在 Effect 中重新建立订阅
- 如何判断逻辑应该放在事件处理函数还是 Effect?
- 用户具体操作触发的逻辑放在事件处理函数中
- 因组件展示而需要与外部系统保持同步的逻辑放在 Effect 中