The power of React refs
Tags: javascript, react, tutorial, webdev
Published: 2022-05-18 10:04:40


In the official React definition “Refs provide a way to access DOM nodes or React elements created in the render method”.

In this article I will show you how Refs can be used for other purposes. 

I will not dwell on the explanation of the concept or the various methods of creating refs. You can find all this on the following link: [https://reactjs.org/docs/refs-and-the-dom.html](https://reactjs.org/docs/refs-and-the-dom.html)


## Standard use case

In a common scenario we can use refs like this:
```javascript
function NormalUsage() {
    const inputRef = React.useRef();

    const handleSubmit = (e) => {
        e.preventDefault();
        console.log(inputRef.current.value);
    };

    return (
        <form onSubmit={handleSubmit}>
            <input type="text" name="input" ref={inputRef} />
            <button> Submit form </button>
        </form>
    );
}
```
In this case, ref is used as a reference to a DOM Input.
 *inputRef* allow us access to input properties like *value.*

## Advanced use — Timeout

The next example is a simple notice component. The notice can be closed by clicking a button, otherwise it will automatically close after a time limit.
```javascript
const TIMEOUT_LIMIT = 5000

function Notice() {
  const [isVisible, setIsVisible] = React.useState(true)
  const timeoutRef = React.useRef(null)

  const hideNotice = () => {
    setIsVisible(false)
  }

  const forceHideNotice = () => {
    clearTimeout(timeoutRef.current)
    hideNotice()
  }

  React.useEffect(() => {
    timeoutRef.current = setTimeout(hideNotice, TIMEOUT_LIMIT)

    return () => {
      clearTimeout(timeoutRef.current)
    }
  }, [])

  return isVisible ? (
    <div className="Notice">
      <div>Notice content</div>
      <button onClick={forceHideNotice}>Close</button>
    </div>
  ) : null
}
```

In this case the ref is used to store a mutable data: the *timeout ID*

The steps are:

 1. Create the ref (*line 6*)

 2. Assign the timeout value to ref (*line 18*)

 3. Clear timeout:
- If notice is closed by the user (*line 13*)
- When component is unmounted (*line 21*)

## Advanced use #2— RequestAnimationFrame

The next example is a simple component that renders a div with an animation on the height.
```javascript
const HEIGHT_LIMIT = 100

function Animated() {
  const divRef = React.useRef()
  const requestRef = React.useRef()
  const heightRef = React.useRef(0)

  const animate = () => {
    heightRef.current += 1
    divRef.current.style.height = `${heightRef.current}px`
    if (heightRef.current < HEIGHT_LIMIT) {
      requestRef.current = requestAnimationFrame(animate)
    }
  }

  React.useEffect(() => {
    requestRef.current = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(requestRef.current)
  }, [])

  return <div ref={divRef} style={{ backgroundColor: "#000" }} />
}
```


In this example 3 refs was created:

*divRef*: A classical use of refs. I’ts a reference to the Div element below.

*requestRef:* It contains the requestAnimationFrame id

*heightRef:* It contains the updated height value

The animate function is called until *heightRef.current* reaches the value of **HEIGHT_LIMIT**. *heightRef.current* is incremented by 1 each time the function is called.

If the component will be unmounted before *heightRef.current* reaches the value of HEIGHT_LIMIT, *cancelAnimationFrame* function will be executed

## Advanced use #3 — Custom Hook

The next example creates a hook that returns a ref containing the x, y position of the mouse. With the help of the requestAnimationFrame a rounded div is animated to follow the mouse

```javascript
//Hook
function useMousePosition() {
  const mousePosition = React.useRef({ x: 0, y: 0 })

   React.useEffect(() => {     
    const updateMousePosition = (ev) => {
      mousePosition.current = { x: ev.clientX, y: ev.clientY }
    }

    window.addEventListener("mousemove", updateMousePosition)
    return () => window.removeEventListener("mousemove", updateMousePosition)     
  }, [])

  return mousePosition
}

//Component
function Point() {
  const mousePosition = useMousePosition()
  const requestRef = React.useRef()
  const pointRef = React.useRef()

  const animate = () => {
    pointRef.current.style.left = `${mousePosition.current.x}px`
    pointRef.current.style.top = `${mousePosition.current.y}px`
    requestRef.current = requestAnimationFrame(animate)
  }
  React.useEffect(() => {
    requestRef.current = requestAnimationFrame(animate)
    return () => cancelAnimationFrame(requestRef.current)
  }, [])

  return (
    <div
      ref={pointRef}
      style={{
        position: "fixed",
        width: "20px",
        height: "20px",
        borderRadius: "50%",
        backgroundColor: "#000",
        transform: "translate(-50%, -50%)"
      }}
    />
  )
}
```

The *usePosition* hook adds a **mouseMove event** to the window and stores the **clientX** and **clientY** values.
In the *animate* function of the *Point* component the values x and y are used to animate the top and left properties of the div referenced by *pointRef*

## Conclusion

React refs are extremely useful for saving mutable data without triggering a re-render of the component.
They are powerful even when used in custom hooks and guarantee high performance.