React useRef

useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
source

The ref object is extremely powerful as it stores a mutable object that changes and does not trigger a component render. This can help our component performance!

Before hooks we had createRef which has the same functionality as useRef but for class components.

import React, { Component, createRef } from "react";

class EmailInput extends Component {
  emailInput = createRef();

  focusEmailInput = () => this.emailInput.current.focus();

  render() {
    return (
      <>
        <input type="email" ref={this.emailInput} />
        <button onClick={this.focusEmailInput}>Focus on the email input!</button>
      </>
    );
  }
}
createRef class component
import React, { useRef } from "react";

const EmailInput = () => {
  const emailInput = useRef();

  const focusEmailInput = () => emailInput.current.focus();

  return (
    <>
      <input type="email" ref={emailInput} />
      <button onClick={focusEmailInput}>Focus on the email input</button>
    </>
  );
}
useRef hook functional component

Let's have a look at a real-world situation.

Imagine you need to design a custom upload button and the default input type file doesn't meet all your requirements but it does have the file system dialog and extension configuration.

<input type="file"
       id="avatar" name="avatar"
       accept="image/png, image/jpeg">
file input

We want to use a material-ui icon button for uploading a file and we are going to use useRef to do that.

import { useRef, ChangeEventHandler } from 'react'
import { IconButton } from '@mui/material'
import InsertPhotoOutlinedIcon from '@mui/icons-material/InsertPhotoOutlined'


export default function MyCustomUploadButton() {
    const fileInputRef = useRef<HTMLInputElement>(null)

    const onFileChange: ChangeEventHandler<HTMLInputElement> = (e) => {
        const reader = new FileReader();
        reader.onload = function (event) {
            //@ts-ignore
            var imgObj = new Image();
            //@ts-ignore
            imgObj.src = event.target.result;
            imgObj.onload = function () {
                dispatch(addImg(imgObj))
            }
        }
        //@ts-ignore
        reader.readAsDataURL(e.target.files[0]);

    }

    const openFileDialog = () => {
        fileInputRef.current?.click()
    }

    return (
        <IconButton color={'primary'} >
            <InsertPhotoOutlinedIcon onClick={openFileDialog} width={'100%'} />
            <input onChange={onFileChange} style={{ display: 'none' }} ref={fileInputRef} type={'file'} />

        </IconButton>
    )
}
Material UI upload button 

Upload button demo

As you can see in our MyCustomUploadButton component, we defined material icon button that triggers input file click on click. the rest of the functionality is handled by the input file onChange. The input itself is never displayed on the screen.

This example above is 1 of many use cases where useRef can come in handy!.

Hope this article helped you and best of luck!