November 22, 2022

React useRef Hook With Examples

In this tutorial you'll see what is useRef() hook in React, how to use it and where to use it.

useRef() hook in React

useRef is a built-in hook in React which is used to reference a value that can be persisted across re-renders.

Syntax of useRef hook is as given below-

const ref = useRef(initialValue)

useRef returns an object with a single property named 'current'. Initially this property current is set to the initialValue you have passed. It can be a value of any type.

You can later set the value of ref.current property to something else.

Important points about useRef hook

  1. The returned reference object will persist for the full lifetime of the component, which means it won't change between component re-renderings.
  2. You can change its current property to store information and read it later. This is possible because reference object is persisted across all renders.
  3. Changing the current property doesn’t trigger a component re-rendering. That’s how useRef differs from useState hook where state change triggers a re-render.

useRef React Examples

Two use cases where useRef can be used are.

  1. Directly accessing DOM nodes.
  2. Persisting a value across renders.

Directly accessing DOM nodes

You can use useRef hook in combination with ref attribute to get access to the DOM nodes. Once you have access to the DOM node you can perform operations like focusing on an element, getting value of an element.

In the example we'll have a form with two input elements for entering user name and city. We'll use refs-

  1. To focus on the name input field.
  2. To access values of the input elements so that we can do some basic validation like elements have some value or not.

Steps to access DOM elements using refs are as follows

  1. Define a ref object using useRef() hook. For example const cityRef = useRef();
  2. Pass the ref object to the ref attribute of the element <input type="text" id="city" ref={cityRef} />
  3. Once the association is done cityRef.current stores that DOM node. So, you have access to <input>’s DOM node.

UserDetails.js (Uses react-bootstrap for styling)

import { useEffect, useRef, useState } from "react";
import { Alert, Card } from "react-bootstrap";

const UserDetails = () => {
    const nameRef = useRef();
    const cityRef = useRef();
    const [error, setError] = useState({show:false,
                                        title:'',
                                    errorMsg:''});
    useEffect(() => {
        nameRef.current.focus();
    }, []);
    const submitHandler = (event) => {
        event.preventDefault();
        const enteredName = nameRef.current.value;
        const enteredCity = cityRef.current.value;

        if(enteredName.trim() === '' || enteredCity.trim() === ''){
            setError({show:true,
                title:'Invalid Input',
                errorMsg:'Please enter a valid value for both name and city'});
            return;
        }
        console.log('Name ' + nameRef.current.value);
        console.log('City ' + cityRef.current.value);
        nameRef.current.value = '';
        cityRef.current.value = '';
    }
    const handleClose = () => {
        setError({show:false,
            title:'',
        errorMsg:''});
    }
    return (
        <Card className="mt-3 mx-auto" style={{ width: '25rem' }}>
            <form onSubmit={submitHandler}>
                    <Alert show={error.show} variant="danger" onClose={handleClose} dismissible>
                    <Alert.Heading>{error.title}</Alert.Heading>
                    <p>{error.errorMsg}</p>
                </Alert>
                <label htmlFor="name">User Name</label>
                <input type="text" id="name" 
                    placeholder="Enter user name" ref={nameRef} />
                <br/>
                <label htmlFor="city">City</label>
                <input type="text" id="city" 
                    placeholder="Enter city" ref={cityRef} />
                    <br/><br/>
                <div>
                    <button onClick={submitHandler}>Submit</button>
                </div>
            </form>
        </Card>
    )
}

export default UserDetails;
  1. In the code, element focusing is done inside the useEffect() hook. Since nameRef.current stores reference to the name input node, when the component mounts, you can call focus() on the html element using this ref.
  2. nameRef.current and cityRef.current store references to the name and city input elements respectively. By accessing value attribute of these elements, you can get values of these inputs and do the required validation. This logic is written in submitHandler() function.
  3. useState() hook is used to set the error state. An error object is used which has these properties show, title, errorMsg. Error message is displayed if show property is set to true.

Persisting a value across renders

useRef can store a mutable value across different renders. How that can be useful? Let's try to understand with an example.

Suppose you have written a component Timer to show current time and you are using setInterval() JavaScript function to call the given function after given time interval (1 second in our case). There is also a button to stop refreshing time. Button click triggers an event handler where we need to call clearInterval() function.

There is one problem though, how to get access to myInterval in stopTimer function as myInterval is not in scope there.

import { useEffect, useRef, useState } from "react";
const Timer = () => {
    const [time, setTime] = useState(new Date());
    useEffect(() => {
        const myInterval = setInterval(() => {
            setTime(new Date());
        }, 1000);
    });
    const stopTimer = () => {
        //Needs access to id (myInterval) to clear interval
    }
    return (
        <div>
            Time is: {time.toLocaleTimeString()}
            <br />
            <button onClick={stopTimer}>Stop</button>
        </div>
    );
}

export default Timer;

That's where you can use useRef hook to store myInterval id so that it can be accessed anywhere in the component and retains its value during re-renders.

import { useEffect, useRef, useState } from "react";

const Timer = () => {
    const [time, setTime] = useState(new Date());
    const intervalRef = useRef();
    useEffect(() => {
        const myInterval = setInterval(() => {
            setTime(new Date());
        }, 1000);
        intervalRef.current = myInterval;
        return () => clearInterval(intervalRef.current);
    });
    const stopTimer = () => {
        //Needs access to id (myInterval) to clear interval
        clearInterval(intervalRef.current);
    }
    return (
        <div>
            Time is: {time.toLocaleTimeString()}
            <br />
            <button onClick={stopTimer}>Stop</button>
        </div>
    );
}

export default Timer;
In the code useRef returns a reference object intervalRef which is then used to store the myInterval id.
intervalRef.current = myInterval;
When interval has to be cleared same reference object can be accessed.
 
clearInterval(intervalRef.current);

That's all for the topic React useRef Hook With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment