In React, changes in state force a component to re-render. A ref is for something you want to store that won't force a re-render even if does change. Typically, a DOM node.

Making this work requires a bit of indirection. The ref itself is always the same object. However the ref's properties - i.e. the current attribute - may change.

Answer from David L. Walsh on Stack Overflow
🌐
React
react.dev › reference › react › useRef
useRef – React
useRef returns a ref object with a single current property initially set to the initial value you provided.
🌐
W3Schools
w3schools.com › react › react_useref.asp
React useRef Hook
In the useEffect, we are updating the useRef current value each time the inputValue is updated by entering text into the input field.
Discussions

What is the .current in a React ref?
ref must be an object that would be passed by reference and not created anew each time. therefore, there must be a property. More on reddit.com
🌐 r/reactjs
37
36
November 13, 2023
reactjs - Why is .current null for useRef Hook? - Stack Overflow
No, rlvRef.current is not a read only value. The reference is a const but .current is mutable. 2022-06-27T18:45:07.613Z+00:00 ... Anybody stumbling over this: In Typescript, try: useRef(null) (if the element is a ). Weirdly, adding | null to the type fixes the error. More on stackoverflow.com
🌐 stackoverflow.com
reactjs - React: Changing `useRef.current` value of ref not triggering useEffect - Stack Overflow
We didn’t choose useRef in this example because an object ref doesn’t notify us about changes to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. More on stackoverflow.com
🌐 stackoverflow.com
Should not set the useRef current value during rendering
The suggested way of storing the store in a useRef does not follow the guidelines from React. That is, we are not supposed to set the value of current during rendering. More on github.com
🌐 github.com
2
March 29, 2024
Top answer
1 of 3
1

In React, changes in state force a component to re-render. A ref is for something you want to store that won't force a re-render even if does change. Typically, a DOM node.

Making this work requires a bit of indirection. The ref itself is always the same object. However the ref's properties - i.e. the current attribute - may change.

2 of 3
1

It might look like unnecessary in your example, where you only read the ref, but it becomes important and understandable, if you consider that you also want to set a value to the ref.

If the react-people would have implemented useRef without the .current, it actually would work if you never have to set the value (in runtime). But then you wouldn't need a ref at all. E.g. then your example could perfectly fine be written as just:

const myVar = 'Hello world!';
return <h1>{myVar}</h1>

But you always need to set some value to the ref, and that would just be not possible without the .current.

Here I show some examples to illustrate what would have happened, if the react-people would have done it without .current:

example 1: set myVar.current

e.g. consider this working example:

// in the first run of the component:
const myVar = useRef('old value'); // myVar === useRef_returnValue === { current: 'old value' }
myVar.current = 'new value';       // myVar === useRef_returnValue === { current: 'new value' }

// in the next run of the component:
// myVar is a reference to the same,
// now changed useRef_returnValue === { current: 'new value' }
const myVar = useRef('old value'); 

// (Note that 'old value' in useRef('old value') is only the initial value,
// which doesn't matter anymore after the first run.)

That would not work if it was without the .current:

// in the first run of the component:
let myVar = useRef('old value');  // myVar === useRef_returnValue === 'old value'
myVar = 'new value';              // myVar is a completely new string 'new value', no reference to the useRef_returnValue anymore.

// in the next run of the component:
// myVar is a reference to the same,
// unchanged useRef_returnValue, still with value 'old value'.
let myVar = useRef('old value');

example 2: ref-property

Even in cases where you might think you never want to set a value, e.g.:

const inputRef = useRef();
return <input ref={ inputRef } />;  // input.ref becomes the internal useRef-object

the input component needs something it can attach itself to. E.g. a fictional implementation might look something like:

const input = function( props ){
  const thisDomObject = thisDomObject();
  if( props.ref ){
    // input.ref.current becomes thisDomObject,
    // input.ref is the internal useRef-object, so
    // (useRef-object).current also becomes thisDomObject
    props.ref.current = thisDomObject;
  }
};

This would not work without the .current:

// input.ref was the internal useRef-object, and now becomes thisDomObject.
// The internal useRef-object stays unchanged.
props.ref = thisDomObject;

Remark:

I think the property name "current" is just a more or less arbitrarily choosen name that doesn't matter. The only important thing is, that it is a property.

🌐
React
react.dev › learn › referencing-values-with-refs
Referencing Values with Refs – React
Inside your component, call the useRef Hook and pass the initial value that you want to reference as the only argument. For example, here is a ref to the value 0: ... You can access the current value of that ref through the ref.current property.
🌐
React
legacy.reactjs.org › docs › hooks-reference.html
Hooks API Reference – React
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue).
🌐
Hygraph
hygraph.com › blog › react-useref-a-complete-guide
React useRef() - A complete guide | Hygraph
January 21, 2026 - When React encounters a useRef() Hook, it returns a plain JavaScript object with a single property: current.
🌐
Refine
refine.dev › home › blog › tutorials › understanding the react useref hook
Understanding the React useRef Hook | Refine
October 16, 2024 - The useRef hook takes an initial value as an argument and returns a ref object. React will set the ref object's current property to the initial value. If you don't pass an initial value, the value of the current property will initially be undefined.
Find elsewhere
🌐
React
react.dev › learn › manipulating-the-dom-with-refs
Manipulating the DOM with Refs – React
Declare inputRef with the useRef Hook. Pass it as <input ref={inputRef}>. This tells React to put this <input>’s DOM node into inputRef.current. In the handleClick function, read the input DOM node from inputRef.current and call focus() on ...
🌐
React Native Express
reactnative.express › react › hooks › useref
useRef
With useRef we can create and update a single mutable value that exists for the lifetime of the component instance. After assigning the ref to a variable, we use .current to access the mutable value.
Top answer
1 of 4
36

I know I am a little late, but since you don't seem to have accepted any of the other answers I'd figure I'd give it a shot too, maybe this is the one that helps you.

Shouldn't it be when useRef.current changes, the stuff in useEffect gets run?

Short answer, no.

The only things that cause a re-render in React are the following:

  1. A state change within the component (via the useState or useReducer hooks)
  2. A prop change
  3. A parent render (due to 1. 2. or 3.) if the component is not memoized or otherwise referentially the same (see this question and answer for more info on this rabbit hole)

Let's see what happens in the code example you shared:

export default function App() {
  const myRef = useRef(1);
  useEffect(() => {
    console.log("myRef current changed"); // this only gets triggered when the component mounts
  }, [myRef.current]);
  return (
    <div className="App">
      <button
        onClick={() => {
          myRef.current = myRef.current + 1;
          console.log("myRef.current", myRef.current);
        }}
      >
        change ref
      </button>
    </div>
  );
}

Initial render

  • myRef gets set to {current: 1}
  • The effect callback function gets registered
  • React elements get rendered
  • React flushes to the DOM (this is the part where you see the result on the screen)
  • The effect callback function gets executed, "myRef current changed" gets printed in the console

And that's it. None of the above 3 conditions is satisfied, so no more rerenders.

But what happens when you click the button? You run an effect. This effect changes the current value of the ref object, but does not trigger a change that would cause a rerender (any of either 1. 2. or 3.). You can think of refs as part of an "effect". They do not abide by the lifecycle of React components and they do not affect it either.

If the component was to rerender now (say, due to its parent rerendering), the following would happen:

Normal render

  • myRef gets set to {current: 1} - Set up of refs only happens on initial render, so the line const myRef = useRef(1); has no further effect.
  • The effect callback function gets registered
  • React elements get rendered
  • React flushes to the DOM if necessary
  • The previous effect's cleanup function gets executed (here there is none)
  • The effect callback function gets executed, "myRef current changed" gets printed in the console. If you had a console.log(myRef.current) inside the effect callback, you would now see that the printed value would be 2 (or however many times you have pressed the button between the initial render and this render)

All in all, the only way to trigger a re-render due to a ref change (with the ref being either a value or even a ref to a DOM element) is to use a ref callback (as suggested in this answer) and inside that callback store the ref value to a state provided by useState.

2 of 4
17

use useCallBack instead, here is the explanation from React docs:

We didn’t choose useRef in this example because an object ref doesn’t notify us about changes to the current ref value. Using a callback ref ensures that even if a child component displays the measured node later (e.g. in response to a click), we still get notified about it in the parent component and can update the measurements.

Note that we pass [] as a dependency array to useCallback. This ensures that our ref callback doesn’t change between the re-renders, and so React won’t call it unnecessarily.

function MeasureExample() {
  const [height, setHeight] = useState(0);

  const measuredRef = useCallback(node => {
    if (node !== null) {
      setHeight(node.getBoundingClientRect().height);
    }
  }, []);

  return (
    <>
      <h1 ref={measuredRef}>Hello, world</h1>
      <h2>The above header is {Math.round(height)}px tall</h2>
    </>
  );
}
🌐
Dmitri Pavlutin
dmitripavlutin.com › react-useref
React useRef() Hook Explained in 3 Steps - Dmitri Pavlutin
April 3, 2023 - Calling const reference = useRef(initialValue) with the initial value returns a special object named reference. The reference object has a property current: you can use this property to read the reference value reference.current, or update ...
🌐
Kinsta®
kinsta.com › home › resource center › blog › react › understanding the useref hook in react
Understanding the useRef Hook in React
3 weeks ago - This allows you to reference and access the element using the myRef variable elsewhere in the component. To access the value stored in the reference created, you can use the .current property of the myRef object.
🌐
GitHub
github.com › reduxjs › redux › issues › 4690
Should not set the useRef current value during rendering · Issue #4690 · reduxjs/redux
March 29, 2024 - Do not write or read ref.current during rendering. ... const storeRef = useRef<AppStore | null>(null) if (!storeRef.current) { storeRef.current = makeStore() storeRef.current.dispatch(initializeCount(count)) }
Author   raRaRa
🌐
Thoughtspile
thoughtspile.github.io › 2021 › 10 › 25 › useref-no-current
Can we useRef, but without the .current? Let's try!
October 25, 2021 - The wrapper object, also known ... stuck with a stale one. So, useRef lets us persist a mutable value between re-renders by putting it into a current property of a constant-reference box object....
🌐
Mimo
mimo.org › glossary › react › useref-hook
React useRef Hook: Syntax, Usage, and Examples
The useRef hook in ReactJS lets you persist values between renders without triggering a re-render. It’s often used to access and modify DOM elements directly, work with state variables, or store mutable values for React components that need to reference the current value without causing updates.
🌐
Medium
medium.com › zestgeek › understanding-the-useref-hook-in-react-real-life-examples-98339ab7f768
Understanding the useRef Hook in React: Real-Life Examples | by Love Trivedi | ZestGeek | Medium
August 12, 2024 - The useRef hook returns a mutable object with a .current property that you can use to store a value. Unlike useState, updating a useRef value does not trigger a component re-render.
🌐
Smashing Magazine
smashingmagazine.com › 2020 › 11 › react-useref-hook
A Thoughtful Way To Use React’s useRef() Hook — Smashing Magazine
Sure, this is an anti-pattern, because we are taking advantage of the flexibility of useRef hook to store local states, and still calling useState hook to ensure the children get the latest value of the useRef variable current value both of which can be achieved with useState.
🌐
React
legacy.reactjs.org › docs › refs-and-the-dom.html
Refs and the DOM – React
function CustomTextInput(props) { // textInput must be declared here so the ref can refer to it const textInput = useRef(null); function handleClick() { textInput.current.focus(); } return ( <div> <input type="text" ref={textInput} /> <input type="button" value="Focus the text input" onClick={handleClick} /> </div> ); } In rare cases, you might want to have access to a child’s DOM node from a parent component.