Type to generate custom UI components with AI

Type to generate UI components from text

OR

Browse thousands of MUI, Tailwind, React components that are fully customizable and responsive.

Explore Components

Exploring the Versatility of React’s useRef: A Practical Exploration

Introduction to React useref

React Hooks, introduced in React 16.8, revolutionized the way we write React components. Before Hooks, state and other React features were only available in class components. Hooks brought these capabilities to functional components, making them more powerful and flexible. Among these hooks, react useRef stands out for its unique abilities and use cases.

React useRef – Video Resources

Are you searching for ways to enhance your team’s productivity? Explore Purecode AI, an AI custom component generator that covers everything from HTML and CSS to Tailwind, JavaScript, and more.

Importance of useRef in React

The useRef Hook in React is essential for two primary reasons: it provides a way to access DOM elements directly and to persist data across renders without triggering re-renders. This makes useRef invaluable for certain tasks where direct DOM manipulation is needed or when you want to keep track of some mutable data without causing a component to re-render every time that data changes.

In the next sections, we’ll dive deeper into useRef, exploring its definition, use cases, and nuances, backed by practical examples and code snippets.

Understanding useRef in React

In this section, we’ll delve into the useRef hook in React, a powerful tool often used in functional components. We’ll define useRef, explore its purpose, and compare it with traditional ref usage in React. This exploration will include practical code examples, demonstrating how useRef operates in various scenarios, from accessing DOM elements to storing mutable values.

Definition and Purpose

The useRef hook in React is used to create a mutable ref object whose .current property is initialized to the passed argument (the initial value). This hook is part of the React Hooks API and is primarily used in functional components, although it can also be useful in class components.

import React, { useRef } from 'react';

function MyComponent() {
  const myRef = useRef(null);
  // ...
}

In the above example, myRef is a ref object created by the useRef hook. The initial value of null is assigned to myRef.current. This ref object can then be used to store a reference to a DOM node or any other mutable value.

Comparison with Traditional Ref Usage in React

Before the introduction of Hooks, React primarily used callback refs and the ref attribute to access DOM elements in class components. The useRef hook simplifies this process in functional components, providing a more straightforward way to create references.

class MyOldComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }

  render() {
    return <div ref={this.myRef}></div>;
  }
}

In the class component example above, this.myRef is created using React.createRef(), a traditional way of creating refs in class components. In contrast, the useRef hook in functional components eliminates the need for constructor and simplifies the syntax.

The useRef hook maintains the same ref object throughout the lifecycle of the component, ensuring that the reference value persists across re-renders. This persistence is crucial for tasks like accessing DOM elements or storing values that should not trigger a component re-render when they change.

The Initial Value of React useRef

In this section, we’ll explore how to initialize the useRef hook in React and provide comprehensive examples to illustrate its usage. Understanding the initial value of useRef is crucial, as it sets the foundation for how the ref object is used within functional components, impacting tasks like accessing DOM elements, managing mutable values, and interacting with child components.

How to Initialize useRef

The useRef hook is initialized by passing the initial value to it. This value is then accessible through the .current property of the returned object. The initial value can be anything – a DOM node, an integer, a string, or even an object. It’s important to note that unlike state variables in React, updating the .current property does not trigger a re-render of the component.

import React, { useRef } from 'react';

function MyComponent() {
  const myRef = useRef(0); // Initial value set to 0
  // ...
}

In the above code, myRef is initialized with an initial value of 0. This value can be accessed or updated via myRef.current.

Examples of Initial Values

Storing a DOM Node Reference: Often, useRef is used to store a reference to a DOM element. This is particularly useful for managing focus, animations, or integrating with third-party DOM libraries.

function TextInputWithFocusButton() {
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };

  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}
After clicking on the Button to focus on the textfield

In this example, inputEl is used to store a reference to the input element. When the button is clicked, the input field is focused programmatically.

Tracking State Without Re-rendering: useRef can be used to keep track of a value that doesn’t require the component to re-render when it changes, such as a timer ID in a stopwatch example.

function Stopwatch() {
  const timerIdRef = useRef(null);

  const startTimer = () => {
    if (!timerIdRef.current) {
      timerIdRef.current = setInterval(() => console.log('Timer tick'), 1000);
    }
  };

  const stopTimer = () => {
    clearInterval(timerIdRef.current);
    timerIdRef.current = null;
  };

  return (
    <div>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
    </div>
  );
}

Here, timerIdRef holds the ID of the timer. It’s used to ensure that the timer can be stopped correctly. This use of useRef is ideal for values that need to persist across renders but don’t directly impact the rendering.

Before starting/Stoping
After Starting.

By understanding how to initialize and use the useRef hook with different initial values, developers can effectively manage direct DOM interactions and mutable values within their functional components. This understanding is pivotal for optimizing component behavior and enhancing the user experience in React applications.

Consider using Purecode AI to access over 10000+ AI-generated ready-made templates and components to speed up your development process. Avoid the hassle with Purecode.

useRef vs. useState: Understanding the Differences

In this section, we’ll compare useRef and useState, two hooks provided by React for managing state and references within functional components. Understanding the differences between these hooks is crucial for optimizing performance and ensuring correct data handling in React applications.

React Hooks – Video Resources

When to Use useRef Over useState

Let’s study when to use the useRef over the useState hook.

Accessing DOM Elements:

useRef is the preferred choice for accessing DOM elements. It allows you to directly interact with a DOM node, which useState cannot do. This is particularly useful for tasks like focusing an input element or integrating with third-party DOM libraries.

function TextInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

In this example, useRef is used to focus an input element when the component mounts, a task that useState is not designed for.

Storing Mutable Values

When you need to store a mutable value that does not cause the component to re-render, useRef is the appropriate choice. This is useful for values like timers, intervals, or any other values that should persist across renders but do not directly impact the render output.

function TimerComponent() {
  const timerId = useRef(null);

  const startTimer = () => {
    timerId.current = setTimeout(() => console.log("Timer done"), 1000);
  };

  return <button onClick={startTimer}>Start Timer</button>;
}

Here, timerId is a mutable value stored in useRef, which does not cause the component to re-render when it changes.

Performance Implications

Here are some performance implications we have to know about.

React Rendering – Video Resources

Re-rendering Behavior:

A key difference between useRef and useState is their impact on component re-rendering. Updating a state variable using useState always causes the component to re-render. In contrast, changing the .current property of the object returned by useRef does not trigger a re-render.

function Counter() {
  const [count, setCount] = useState(0); // State causes re-render
  const countRef = useRef(0); // Ref does not cause re-render

  const increment = () => {
    setCount(count + 1); // This will re-render the component
    countRef.current += 1; // This will not re-render the component
  };

  return <button onClick={increment}>Increment</button>;
}

In the Counter component, updating count with setCount will re-render the component, while updating countRef.current will not.

Mutable vs. Immutable

useState manages immutable state, meaning every state update creates a new state value and potentially causes a component re-render. useRef, on the other hand, holds a mutable .current property, which can be updated without creating a new object, thus not causing additional renders.

Developers can make informed decisions about when to use useRef or useState based on the specific requirements of their React components when they understand these differences. This knowledge is essential for writing efficient, performant React applications that effectively manage DOM interactions and state changes.

Accessing DOM Elements with React useRef

In this section, we explore the use of useRef for accessing and manipulating DOM elements in functional components. This is a key application of useRef in React, enabling direct interaction with the DOM in a way that aligns with React’s principles.

Practical Examples of DOM Manipulation

A common use case for useRef is to dynamically adjust the width of a DOM element. This can be particularly useful in responsive designs or interactive UIs.

function AdjustableWidthComponent() {
  const divRef = useRef(null);

  const increaseWidth = () => {
    divRef.current.style.width = `${divRef.current.offsetWidth + 10}px`;
  };

  return (
    <div>
      <div ref={divRef} style={{ width: '100px', height: '50px', backgroundColor: 'lightblue' }}>
        Sample Div
      </div>
      <button onClick={increaseWidth}>Increase Width</button>
    </div>
  );
}
Before increase
After Increase

In this example, divRef is attached to a div element. When the button is clicked, the increaseWidth function is triggered, increasing the width of the div by 10 pixels each time.

Toggling Element Visibility

Another practical use of useRef is to toggle the visibility of a DOM element. This can be useful in scenarios like accordions, modals, or simply showing/hiding content based on user interaction.

function ToggleVisibilityComponent() {
  const contentRef = useRef(null);

  const toggleVisibility = () => {
    const currentDisplay = contentRef.current.style.display;
    contentRef.current.style.display = currentDisplay === 'none' ? 'block' : 'none';
  };

  return (
    <div>
      <button onClick={toggleVisibility}>Toggle Content</button>
      <div ref={contentRef} style={{ display: 'none' }}>
        This content can be toggled.
      </div>
    </div>
  );
}
Toggling the content will display it for us

Here, contentRef is used to reference a div element whose visibility is toggled by the toggleVisibility function.

Limitations and Best Practices

While useRef provides a straightforward way to access and manipulate DOM elements, it’s important to use this power judiciously:

  • Avoid Overuse: Direct DOM manipulation should be limited in React applications. React’s declarative approach should be the primary method for DOM updates. Use useRef only when necessary.

  • Synchronization with State: When DOM changes might affect the component’s state or props, ensure that these updates are synchronized to prevent inconsistencies.

  • Accessibility Considerations: Direct DOM manipulations might bypass some of the accessibility features that React provides. Always consider accessibility implications when manipulating the DOM.

useRef for Storing Mutable Values

In this section, we’ll explore how useRef can be used to store mutable values in functional components. This aspect of useRef is particularly useful for values that need to persist across renders without causing additional re-renders.

How useRef Can Hold Mutable Data

useRef is not just for referencing DOM nodes. It can also be used to keep track of any mutable data. The key feature of useRef in this context is that the .current property can hold any value and doesn’t trigger re-renders when the data changes. This makes it ideal for values that need to persist for the lifetime of the component but don’t directly impact the rendering.

function TimerComponent() {
  const elapsedRef = useRef(0);

  useEffect(() => {
    const timer = setInterval(() => {
      elapsedRef.current += 1;
      console.log(`Elapsed time: ${elapsedRef.current} seconds`);
    }, 1000);

    return () => clearInterval(timer);
  }, []);

  // Rest of the component
}

In the TimerComponent example, elapsedRef is used to track the elapsed time. The component does not re-render every second, but the elapsed time is still being tracked and updated.

Use Cases in Functional Components

useRef can be used to keep track of a component’s previous state or props. This is useful in scenarios where you need to compare the current and previous values.

function MyComponent({ value }) {
  const previousValueRef = useRef();

  useEffect(() => {
    previousValueRef.current = value;
  }, [value]);

  return (
    <div>
      Current value: {value} <br />
      Previous value: {previousValueRef.current}
    </div>
  );
}

Here, previousValueRef stores the previous value of the value prop, allowing the component to access it without re-rendering when the prop changes.

Persisting State Across Re-renders

Another use case is to persist some state across re-renders without causing those re-renders. This is particularly useful for data that is used for internal calculations or logic but does not need to be displayed.

function ComplexCalculationComponent() {
  const calculationResultRef = useRef(0);

  const performCalculation = (input) => {
    // Perform some complex calculations
    calculationResultRef.current = /* result of calculation */;
  };

  // Rest of the component
}

In this example, calculationResultRef is used to store the result of a complex calculation. The value is preserved across re-renders but does not itself cause any re-renders.

Advanced Concepts

In this section, we delve into more advanced concepts related to the useRef hook in React. We’ll explore its relationship with other hooks like useCallback, useEffect, and useLayoutEffect, and discuss how to enforce component re-renders using useRef.

useRef vs. useCallback

useRef and useCallback are often compared because both can be used to persist references. However, their use cases differ:

AspectuseRefuseCallback
PurposeUsed to persist a mutable reference across component renders.Used to memorize a callback function and its dependencies.
UsageCommonly used for referencing DOM elements or storing mutable data that doesn’t trigger re-renders.Primarily used to optimize performance by preventing unnecessary re-creation of functions on each render.
Return ValueReturns a mutable ref object with a .current property.Returns a memoized version of the callback function.
Re-renderingUpdating the .current property does not trigger a re-render.The callback function is recreated only when its dependencies change, potentially reducing re-renders.
Exampleconst myRef = useRef(initialValue);const memoizedCallback = useCallback(() => { /* function logic */ }, [dependencies]);
MutabilityThe .current property is mutable and can hold any value.The function itself is immutable, but it’s redefined when dependencies change.
Dependency ArrayNot applicable, as useRef doesn’t accept a dependency array.Accepts a dependency array to determine when to recreate the function.
  • useRef is used to keep a mutable reference to a DOM node or any other value across renders.

  • useCallback, on the other hand, is used to memorize a callback function.

function MyComponent() {
  const ref = useRef(null);
  const callback = useCallback(() => {
    // Function logic here
  }, [/* dependencies */]);

  // ...
}

In this example, ref could be used to reference a DOM node, while callback is a memorized function that will only change if its dependencies change.

useRef with useEffect and useLayoutEffect

useRef can be combined with useEffect and useLayoutEffect for various purposes:

useEffect: Often used with useRef when you need to interact with a DOM element after the component has rendered or to set up subscriptions and timers.

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    if (myRef.current) {
      // Do something with myRef.current
    }
  }, []);

  // ...
}

useLayoutEffect: Similar to useEffect, but it fires synchronously after all DOM mutations. Use this when you need to read layout from the DOM and re-render synchronously.

function MyComponent() {
  const myRef = useRef(null);

  useLayoutEffect(() => {
    if (myRef.current) {
      const { offsetWidth } = myRef.current;
      // Do something with offsetWidth
    }
  }, []);

  // ...
}

Enforcing Component Re-render with useRef

While useRef itself does not cause re-renders, it can be used in conjunction with state hooks to force a component to re-render:

function MyComponent() {
  const [, forceRender] = useState({});
  const forceUpdate = useCallback(() => forceRender({}), []);

  const myRef = useRef(null);

  useEffect(() => {
    myRef.current = setInterval(() => {
      // Do something
      forceUpdate(); // This will cause the component to re-render
    }, 1000);

    return () => clearInterval(myRef.current);
  }, [forceUpdate]);

  // ...
}

In this example, forceUpdate is a function that updates a state, causing the component to re-render. This pattern can be useful in scenarios where you need to update the component in response to non-reactive data changes, like those stored in refs.

Conclusion on React useref

In this article, we’ve explored the useRef hook in React, a versatile tool in the React Hooks API. We began by understanding its basic usage for referencing DOM elements and storing mutable values, and then delved into more advanced concepts, comparing it with other hooks like useState and useCallback.

If you’re looking to speed up your development time, consider checking out PureCode AI. PureCode provides over 10k AI-generated templates that can be integrated into your web pages to help you build UI faster.

Key takeaways include:

  • useRef is essential for accessing DOM nodes and persisting data across renders without triggering re-renders.

  • It differs from useState in that it does not cause component re-renders when its content changes.

  • useRef can be combined with other hooks like useEffect and useLayoutEffect for more complex scenarios.

  • While primarily used in functional components, useRef offers capabilities that extend beyond traditional class component patterns.

Whether it’s managing focus in UI elements, tracking previous state values, or integrating with third-party DOM libraries, useRef proves to be an indispensable tool in the React developer’s toolkit.

Glory Olaifa

Glory Olaifa