DEV Community

Cover image for The power of React refs
Giuseppe Ciullo
Giuseppe Ciullo

Posted on • Edited on

The power of React refs

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

Standard use case

In a common scenario we can use refs like this:

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

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.

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

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.

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" }} />
}
Enter fullscreen mode Exit fullscreen mode

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

//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%)"
      }}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

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.

Oldest comments (3)

Collapse
 
yisusjuarez profile image
YJDev

Your avanced axample is the same as the first one, thank you anyway!

Collapse
 
josephciullo profile image
Giuseppe Ciullo

Thank you for letting me know. I fixed the mistake!

Collapse
 
lico profile image
SeongKuk Han

Thanks for sharing :)