DEV Community

Cover image for The useRef() hook in React!
Ustariz Enzo
Ustariz Enzo

Posted on • Edited on

The useRef() hook in React!

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;
Enter fullscreen mode Exit fullscreen mode

 
In order to use the useRef() hook, let's import it:

import {useRef} from "react";
Enter fullscreen mode Exit fullscreen mode

 
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;
Enter fullscreen mode Exit fullscreen mode

 
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>
    )
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

 
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>
    )
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)
        }
    }

Enter fullscreen mode Exit fullscreen mode

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>
    )
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jennasys profile image
JennaSys

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/...

Collapse
 
fernandomatiasdv profile image
fernandomatiasdv

I love it!! <3 You can explains it in the simple way :D

Collapse
 
ziratsu profile image
Ustariz Enzo

Thanks Fernando, have a nice day mate!

Collapse
 
dommirr profile image
dommirr

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?

Collapse
 
ziratsu profile image
Ustariz Enzo

It depends if you want to use controlled or uncontrolled inputs.
I really don't think that there is a performance problem there :).

Collapse
 
annetawamono profile image
Anneta Wamono

Is it possible to use 1 ref across components that aren't in each other's hierarchy?

Collapse
 
ziratsu profile image
Ustariz Enzo

You need to instanciate one ref by component, you cannot share it;