This is why reading ref.current during render leads to unreliable code. If you need that, use state instead.
Deep DiveHow does useRef work inside?Although both useState and useRef are provided by React, in principle useRef could be implemented on top of useState. You can imagine that inside of React, useRef is implemented like this:
// Inside of Reactfunction useRef(initialValue) { const [ref, unused] = useState({ current: initialValue }); return ref;}During the first render, useRef returns { current: initialValue }. This object is stored by React, so during the next render the same object will be returned. Note how the state setter is unused in this example. It is unnecessary because useRef always needs to return the same object!
React provides a built-in version of useRef because it is common enough in practice. But you can think of it as a regular state variable without a setter. If you’re familiar with object-oriented programming, refs might remind you of instance fields—but instead of this.something you write somethingRef.current.
When to use refsTypically, you will use a ref when your component needs to “step outside” React and communicate with external APIs—often a browser API that won’t impact the appearance of the component. Here are a few of these rare situations:
Storing timeout IDs Storing and manipulating DOM elements, which we cover on the next page Storing other objects that aren’t necessary to calculate the JSX.If your component needs to store some value, but it doesn’t impact the rendering logic, choose refs.
Best practices for refsFollowing these principles will make your components more predictable:
Treat refs as an escape hatch. Refs are useful when you work with external systems or browser APIs. If much of your application logic and data flow relies on refs, you might want to rethink your approach. Don’t read or write ref.current during rendering. If some information is needed during rendering, use state instead. Since React doesn’t know when ref.current changes, even reading it while rendering makes your component’s behior difficult to predict. (The only exception to this is code like if (!ref.current) ref.current = new Thing() which only sets the ref once during the first render.)Limitations of React state don’t apply to refs. For example, state acts like a snapshot for every render and doesn’t update synchronously. But when you mutate the current value of a ref, it changes immediately:
ref.current = 5;console.log(ref.current); // 5This is because the ref itself is a regular JaScript object, and so it behes like one.
You also don’t need to worry about oiding mutation when you work with a ref. As long as the object you’re mutating isn’t used for rendering, React doesn’t care what you do with the ref or its contents.
Refs and the DOMYou can point a ref to any value. However, the most common use case for a ref is to access a DOM element. For example, this is handy if you want to focus an input programmatically. When you pass a ref to a ref attribute in JSX, like , React will put the corresponding DOM element into myRef.current. Once the element is removed from the DOM, React will update myRef.current to be null. You can read more about this in Manipulating the DOM with Refs.
Recap Refs are an escape hatch to hold onto values that aren’t used for rendering. You won’t need them often. A ref is a plain JaScript object with a single property called current, which you can read or set. You can ask React to give you a ref by calling the useRef Hook. Like state, refs let you retain information between re-renders of a component. Unlike state, setting the ref’s current value does not trigger a re-render. Don’t read or write ref.current during rendering. This makes your component hard to predict.