React Refs Useref
015 - React: useRef Hook
The useRef Hook is a fundamental part of React that allows you to create mutable ref objects. These ref objects can hold a reference to a DOM element or any mutable value that persists across re-renders without causing a re-render when its value changes.
Why Use useRef?
useRef serves two primary purposes:
- Accessing the DOM directly: While React encourages a declarative approach to UI, there are times when you need to interact with the DOM directly (e.g., managing focus, text selection, media playback, or integrating with third-party DOM libraries).
- Storing mutable values that don't trigger re-renders:
useRefcan hold any mutable value, similar to an instance variable in a class component. UnlikeuseState, updating aref's.currentproperty does not trigger a re-render of the component. This is useful for storing values that need to persist across renders but whose changes don't need to be reflected in the UI immediately.
How useRef Works
useRef returns a plain JavaScript object with a single property called current. This current property can be initialized with an argument passed to useRef.
Syntax
const myRef = useRef(initialValue);myRef: Therefobject returned byuseRef.myRef.current: The actual mutable value or DOM element reference.initialValue: The initial value formyRef.current.
Example: contentRef in src/pages/BlogPostPage.js
In BlogPostPage.js, useRef is used to get a direct reference to the main content div of the blog post. This reference is then used to calculate the reading progress based on scroll position.
// src/pages/BlogPostPage.js import React, { useState, useEffect, useRef } from 'react'; // ... const BlogPostPage = () => { // ... const contentRef = useRef(null); // Initialize contentRef with null // ... useEffect(() => { const handleScroll = () => { if (contentRef.current) { // Access the DOM element via .current const { scrollTop, scrollHeight, clientHeight } = document.documentElement; const totalHeight = scrollHeight - clientHeight; const currentProgress = (scrollTop / totalHeight) * 100; setReadingProgress(currentProgress); setIsAtTop(scrollTop === 0); } }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [post]); return ( // ... <div ref={contentRef} // Attach the ref to the div element className="prose prose-xl prose-dark max-w-none" > {/* ... Markdown content ... */} </div> // ... ); };Explanation:
const contentRef = useRef(null);: Arefobject namedcontentRefis created and initialized withnull. At this point,contentRef.currentisnull.<div ref={contentRef}>: Therefobject is attached to thedivelement that contains the blog post's Markdown content. Once the component renders, React will setcontentRef.currentto point to this actual DOMdivelement.if (contentRef.current): Inside theuseEffect'shandleScrollfunction,contentRef.currentis checked to ensure that the DOM element is available before attempting to access its properties (likescrollHeightorclientHeight).document.documentElement: WhilecontentRef.currentgives a reference to the specific contentdiv, the scroll calculation here usesdocument.documentElement(the<html>element) to get the overall page scroll position and dimensions. This is a common pattern for tracking global scroll progress.
useRef vs. useState
It's important to understand when to use useRef versus useState:
| Feature | useState | useRef |
|---|---|---|
| Purpose | Manages state that triggers re-renders. | Accesses DOM elements or stores mutable values that don't trigger re-renders. |
| Re-renders | Updates to state variables cause component re-renders. | Updates to ref.current do not cause re-renders. |
| Value Persistence | Value persists across re-renders. | Value persists across re-renders. |
| Mutability | State is generally treated as immutable (updated via setState). | ref.current is directly mutable. |
When to use useRef:
- Managing focus, text selection, or media playback.
- Triggering imperative animations.
- Integrating with third-party DOM libraries.
- Storing any mutable value that you don't want to trigger a re-render when it changes (e.g., a timer ID, a previous value of a prop).
Summary
useRef provides a way to "escape" React's declarative paradigm when necessary, offering direct access to the underlying DOM or a persistent mutable storage for values that don't need to be part of the component's reactive state. It's a powerful tool for specific use cases where direct imperative manipulation or persistent non-state values are required.