Hey fellow creators
The useRef() hook is a way to select elements with React. You could use the usual document.querySelector method, however it's not optimised since it'll look through the entire DOM. Whereas if you use this hook, it'll only look in your component!
If you prefer to watch the video version, it's right here :
1. Let's look at a real example.
Let's imagine you have a form:
import "./App.css";
function App(){
return (
<div className="app">
<form>
<label htmlFor="name">Name</label>
<input type="text" id="name" />
<label htmlFor="email">Email</label>
<input type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
}
export default App;
In order to use the useRef() hook, let's import it:
import {useRef} from "react";
Now, let's create some references, by giving it a name:
import {useRef} from "react";
import "./App.css";
function App(){
const nameRef= useRef();
return (
<div className="app">
<form>
<label htmlFor="name">Name</label>
<input type="text" id="name" />
<label htmlFor="email">Email</label>
<input type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
}
export default App;
Now you can just select an element that you want to add inside that ref with the ref attribute. For example, let's add it to the first input:
return (
<div className="app">
<form>
<label htmlFor="name">Name</label>
<input ref={nameRef} type="text" id="name" />
<label htmlFor="email">Email</label>
<input type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
If you log it, it will simply show an object with a property current as undefined. However, what we want is the value of the current property.
Since the component is executed first, we need to use the hook useEffect() to see the value of our ref.
It's because useEffect callback function is triggered after the creation of the component.
import {useRef, useEffect} from "react";
import "./App.css";
function App(){
const nameRef= useRef();
console.log(nameRef)
useEffect(() => {
console.log(nameRef); // your ref obj
}, [])
return (
<div className="app">
<form>
<label htmlFor="name">Name</label>
<input ref={nameRef} type="text" id="name" />
<label htmlFor="email">Email</label>
<input type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
}
export default App;
You need to feed the useEffect() hook with an arrow function and an empty array, that is the dependency array, it means that useEffect will only be triggered once, after the creation of that component.
2. All right, now create a ref for the second input.
import {useRef, useEffect} from "react";
import "./App.css";
function App(){
const nameRef= useRef();
const mailRef = useRef()
console.log(nameRef)
useEffect(() => {
console.log(nameRef);
}, [])
return (
<div className="app">
<form>
<label htmlFor="name">Name</label>
<input ref={nameRef} type="text" id="name" />
<label htmlFor="email">Email</label>
<input ref={mailRef} type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
}
export default App;
Usually we use Ref with an onSubmit method:
useEffect(() => {
console.log(nameRef);
}, [])
const HandleSubmit = e => {
e.preventDefault()
console.log(nameRef.current.value);
console.log(mailRef.current.value);
}
return (
<div className="app">
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name</label>
<input ref={nameRef} type="text" id="name" />
<label htmlFor="email">Email</label>
<input ref={mailRef} type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
You can add a console.log() if you want to use what is inside your inputs, for example for an API call.
Now, whenever you write inside the inputs and submit the form, the values show up in the console.
3. The multi-ref.
How can you select multiple references if you have too many?
Start a ref with an empty array:
import {useRef, useEffect} from "react";
import "./App.css";
function App(){
const inputs = useRef([]);
const HandleSubmit = e => {
e.preventDefault()
console.log(inputs.current);
}
return (
<div className="app">
<form onSubmit={handleSubmit}>
...
</form>
</div>
)
}
export default App;
Then, create a method that will check if the element is not undefined and if it's not already inside the array, then you'll push it inside of the array:
const addInputs = el => {
if(el && !inputs.current.includes(el)){
inputs.current.push(el)
}
}
Add that method to each of your inputs:
return (
<div className="app">
<form onSubmit={handleSubmit}>
<label htmlFor="name">Name</label>
<input ref={addInputs} type="text" id="name" />
<label htmlFor="email">Email</label>
<input ref={addInputs} type="text" id="email" />
<button>Submit</button>
</form>
</div>
)
Well done! You now know how to use the hook *useRef()* to select your elements!
Check my youtube channel : https://www.youtube.com/c/TheWebSchool
Come and take a look at my Youtube channel: https://www.youtube.com/c/Learntocreate/videos
See you soon!
Enzo.
Top comments (7)
Nice concise write-up! I usually use controlled components and useState but I could see where useRef might be a better fit in many cases.
I did a Python version of your demo code:
github.com/JennaSys/pyuseref/blob/...
I love it!! <3 You can explains it in the simple way :D
Thanks Fernando, have a nice day mate!
I understand that it is an example of how to use useEffect, but I don't know if it is the best example.
Isn't it more expensive for React to save the multi-input references than to render multiple inputs controlled with a useState?
It depends if you want to use controlled or uncontrolled inputs.
I really don't think that there is a performance problem there :).
Is it possible to use 1 ref across components that aren't in each other's hierarchy?
You need to instanciate one ref by component, you cannot share it;