Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
useRef 如何跨渲染保持可变值并提供直接的 DOM 访问。
useRef 返回一个可变对象 { current: value },它在组件的整个生命周期中持久存在。与 state 不同,改变 ref.current 不会触发重新渲染。React 还将 refs 用作直接 DOM 访问的逃生舱口 — 在 commit 阶段之后,React 将 ref.current 设置为实际 DOM 节点,让你可以命令式地访问浏览器 API。
第一次渲染时,useRef(initialValue) 创建对象 '{' current: initialValue '}' 并将其存储在 Fiber 的 hooks 列表上。在每次后续渲染中,React 返回相同的对象 — 因此 ref.current 始终指向同一个盒子。
React 不跟踪 ref.current 的改变。当你写 ref.current = 5 时,React 不知道它改变了,也不会调度重新渲染。这使 refs 非常适合你想持久化但不需要显示的值。
当你将 ref 传递给 JSX 元素(<input ref='{'myRef'}' />)时,React 不会在渲染阶段设置 ref.current。它等到 commit 阶段 — DOM 节点存在后 — 才设置 ref.current = domNode。
当组件卸载时,React 在运行清理 effects 之前将 ref.current = null。这防止你的 effects 访问已分离的 DOM 节点。
默认情况下,refs 不能附加到函数组件。使用 forwardRef 接受 ref prop 并将其转发到 DOM 元素,或通过 useImperativeHandle 暴露命令式方法。
ref 对象。React 在每次渲染时给你相同的对象。自由地改变 current — 不会发生重新渲染。
将 ref 传递给 JSX 元素,以在 commit 后在 ref.current 中获取底层 DOM 节点。适用于聚焦、滚动和测量。
包裹函数组件以接受 ref prop 并将其转发到内部元素或通过 useImperativeHandle 暴露。
自定义父组件访问子组件 ref 时看到的内容。你可以暴露特定方法而不是 DOM 节点。
对影响 UI 的值使用 state(触发重新渲染)。对不影响渲染的值使用 refs:定时器、DOM 节点、之前的值、标志。
1// 1. DOM access — focus an input programmatically2const inputRef = useRef(null);3<input ref={inputRef} />4// After mount: inputRef.current is the <input> DOM node5inputRef.current.focus(); // imperative DOM call67// 2. Persist a value without re-rendering8const timerRef = useRef(null);9timerRef.current = setInterval(tick, 1000);10// Later:11clearInterval(timerRef.current);1213// 3. Read latest state in a stale closure14const countRef = useRef(count);15countRef.current = count; // sync on every render16useEffect(() => {17 const interval = setInterval(() => {18 console.log(countRef.current); // always latest value19 }, 1000);20 return () => clearInterval(interval);21}, []); // empty deps — no stale closure problem
Refs 是 React 命令式代码的官方逃生舱口。它们让你能够将 React 的声明式世界与需要命令式调用的浏览器 API(聚焦、滚动、动画、第三方 DOM 库)桥接起来。它们还解决了 effects 中的陈旧闭包问题 — 通过保持 ref 与最新 state 同步,你可以在长时间运行的异步操作中安全地访问当前值。
A multi-step checkout form should automatically focus the first empty field when the user navigates to a new step. Using state to track focus causes infinite re-renders.
Attempting to manage focus with useState creates a loop: setState triggers re-render, re-render re-focuses, which fires onFocus, which might set state again. Focus is an imperative DOM operation — it doesn't fit React's declarative model.
Use a ref to directly call `.focus()` on the input DOM node: `useEffect(() => { inputRef.current?.focus() }, [currentStep])`. The ref gives you direct DOM access without triggering re-renders. Focus management is inherently imperative — refs are React's bridge to imperative DOM APIs.
Takeaway: Use refs for imperative DOM operations that don't produce visual output (focus, scroll, measure, select text, play/pause media). These operations are side effects that bypass React's virtual DOM — refs give you a controlled escape hatch.
You need to compare the current value with the previous value to show a 'price changed' animation. Storing the previous value in state causes an extra re-render on every change.
Using `useState(previousPrice)` to track the old price means: price changes → re-render with new price → useEffect sets previousPrice → ANOTHER re-render. Two renders for one price change, and the animation triggers late.
Use a ref: `const prevPriceRef = useRef(price)`. In useEffect, compare `price !== prevPriceRef.current`, trigger the animation, then update: `prevPriceRef.current = price`. Refs update without re-rendering — you get the comparison for free, no extra render cycle.
Takeaway: Refs are the correct tool for 'instance variables' — values that need to persist across renders but shouldn't trigger re-renders when updated. Common patterns: previous values, timer IDs, DOM nodes, external library instances, and accumulator values.