What’s the difference between using a ref and a variable to keep track of a value in React?
Table of contents
Sometimes, you want to keep track of a value in React, but you don’t want to store it in the state because it will cause extra renders when it changes. The recommended method (for a functional component) is to use the useRef hook to store the value there.
In the following example, I want to run the effect when the state changes, but not the first time the component mounts. For this reason, I use a firstTime ref that initially is set to true and run the effect only when the firstTime.current is false:
import React, { useState, useEffect, useRef } from "react";
const Component = () => {
const [state, setState] = useState(0);
const firstTime = useRef(true);
useEffect(() => {
if (!firstTime.current) {
// Run the effect.
} else {
firstTime.current = false;
}
}, [state]);
return <div></div>;
};
export default Component;The syntax is a bit ugly because you have to use the .current property, and you might be tempted to use a regular variable instead of the useRef hook, as shown in the next example.
import React, { useState, useEffect } from "react";
let firstTime = true;
const Component = () => {
const [state, setState] = useState(0);
useEffect(() => {
if (!firstTime) {
// Run the effect.
} else {
firstTime = false;
}
}, [state]);
return <div></div>;
};
export default Component;Differences
As long as the component is a singleton—meaning that you use only one instance of the component in your application—both methods do the same thing. Multiple instances, though, which is really common, share the same variable! As a result, you should avoid using a regular variable because it can get pretty bad. The following example illustrates the problem:
import React, { useState } from "react";
let counterOutside = 0;
const Counter = () => {
const [counter, setCounter] = useState(0);
console.log(counterOutside);
return (
<p>
The counter is {counter}{" "}
<button
onClick={() => {
setCounter(counter + 1);
counterOutside = counterOutside + 1;
}}
>
+
</button>
</p>
);
};
export default Counter;// ....
<div>
<Counter />
<Counter />
</div>If you press the button inside one counter, it prints “1 1” where it should print “1 0”, “2, 2” where it should print “2 0”, and so on.
Links
- Hooks FAQ: Is there something like instance variables?
useRefin Hooks API.- The same question in a GitHub issue.
Other things to read
Popular
- Reveal animations on scroll with react-spring
- Gatsby background image example
- Extremely fast loading with Gatsby and self-hosted fonts