DEV Community

Cover image for The Difference between the useState() and useRef() Hooks
Emmanuel Xs
Emmanuel Xs

Posted on

The Difference between the useState() and useRef() Hooks

The useState() Hook

The bread and butter of every React developer. A hook used to manage the state of our application (client) and re-render components when state changes.

The useRef() Hook

A hook that allows you to step outside of the React concept (UI being tied to states, i.e., state changes causing re-renders) and persist values.

Do you know the difference between the two hooks? If yes, are you aware of the nuances and when to use one over the other? If not, don’t worry; you’re in the right place. And for those who have a deep understanding of these hooks, Boss I greet ooh!

a gif showing some raising his two hands up indicating greetings or hailing

The useState() Hook Declaration with Example

import React, { useState } from 'react';

const App = () => {
  const [greeting, setGreeting] = useState(" World");
  console.log(`Hello${greeting}!`);

  return (<p>Hello{greeting}!</p>);
};
Enter fullscreen mode Exit fullscreen mode

When the greeting variable above changes, it triggers a re-render of the entire App component..

Some use Case of the useState() hook

  • Capturing form inputs: Typically done using controlled inputs, where the input value is tied to the component's state, and changes to the input field update the state accordingly.
import React, { useState } from 'react';

const ControlledInput = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (e) => {
    setInputValue(e.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleChange} />
      <p>Current Input: {inputValue}</p>
    </div>
  );
};

export default ControlledInput;
Enter fullscreen mode Exit fullscreen mode

The ControlledInput component uses state to manage the text input's value. Any change to the input field updates the state, with the state updating the displayed value.

  • Show or Hide Components: e.g Modals. tool-tip, drop-down e.t.c.
import React, { useState } from 'react';

const Modal = () => {
  const [isModalVisible, setIsModalVisible] = useState(false);

  const toggleModal = () => {
    setIsModalVisible(!isModalVisible);
  };

  return (
    <div>
      <button onClick={toggleModal}>
        {isModalVisible ? 'Hide' : 'Show'} Modal
      </button>
      {isModalVisible && (
        <div className="modal">
          <p>This is a modal!</p>
          <button onClick={toggleModal}>Close</button>
        </div>
      )}
    </div>
  );
};

export default Modal;
Enter fullscreen mode Exit fullscreen mode

The Modal component uses state to toggle the visibility of a modal. Clicking the button updates the state, which shows or hides the modal.

  • Dynamic styling or rendering components.
import React, { useState } from 'react';

const ShowRed = () => {
  const [isRed, setIsRed] = useState(false);

  const toggleColor = () => {
    setIsRed(!isRed);
  };

  return (
    <div>
      <button onClick={toggleColor}>Toggle Color</button>
      <p style={{ color: isRed ? 'red' : 'blue' }}>
        This text changes color!
      </p>
    </div>
  );
};

export default ShowRed;
Enter fullscreen mode Exit fullscreen mode

The ShowRed component toggles text color between red and blue based on the state variable isRed. Clicking the button changes the state, which updates the text color dynamically.

  • Counters: A classic and popular use case of the useState hook. You’ll often see this example in almost every React tutorial to demonstrate basic state management.
import React, { useState } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

The Counter component displays a count value and provides buttons to increment or decrement it. Clicking the buttons updates the state, which re-renders the component with the new count.

The useRef() hook declaration with example

import React, { useRef } from 'react';

const App = () => {
const greetingRef = useRef("World");
console(`Hello ${greeting}!`);

return (<p>Hello{greeting}!</p>)
//=> Hello World!
Enter fullscreen mode Exit fullscreen mode

When the greetingRef variable above changes it doesn't triggers the re-rendering of the entire App component.

Some use Case of the useRef() hook

  • Accessing DOM Elements:
import React, { useRef } from 'react';

const Input = () => {
  const inputRef = useRef(null);

  const handleFocus = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};

export default Input;
Enter fullscreen mode Exit fullscreen mode

The Input component uses the useRef hook to access a DOM element directly. Clicking the "Focus Input" button triggers handleFocus, which sets focus to the input field using the inputRef.

  • Storing Mutable Values to Avoid Triggering Re-renders: Another alternative is using the useMemo() or declaring the variable outside the component.
import React, { useState, useRef, useEffect } from 'react';

const Timer = () => {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef();

  useEffect(() => {
    intervalRef.current = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

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

  return <div>Seconds: {seconds}</div>;
};

export default Timer;
Enter fullscreen mode Exit fullscreen mode

The Timer component uses the useRef hook to store the interval ID and avoid re-renders. setInterval updates the seconds state every second, and the intervalRef ensures that the interval is cleared on component unmount.

  • Persisting Form Input State: Achieved with uncontrolled inputs, where the value is accessed directly from the DOM via a ref. This allows the input field to operate independently of the component's state and avoids triggering re-renders.
import React, { useRef } from 'react';

const Form = () => {
  const nameRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Name:', nameRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input ref={nameRef} type="text" placeholder="Enter your name" />
      <button type="submit">Submit</button>
    </form>
  );
};

export default Form;

Enter fullscreen mode Exit fullscreen mode

The Form component uses the useRef hook to manage form input as an uncontrolled input. The form value is accessed directly from the DOM using nameRef when submitted, without triggering re-renders.

  • Giving Access to Native HTML Elements DOM to a Hook, Function, or Package:
import React, { useEffect, useRef } from 'react';
import { gsap } from 'gsap';

const AnimatedBox = () => {
  const boxRef = useRef(null);

  useEffect(() => {
    gsap.to(boxRef.current, { x: 100, duration: 2 });
  }, []);

  return <div ref={boxRef} className="box">Animate me</div>;
};

export default AnimatedBox
Enter fullscreen mode Exit fullscreen mode

The AnimatedBox component uses the useRef hook to access a DOM element and animates it with GSAP. The useEffect hook triggers the animation when the component mounts, moving the element 100 pixels to the right over 2 seconds.

The Difference between the two hooks

  • Purpose:

    • useState is used for managing stateful values and causing re-renders when these values change.
    • useRef is used for persisting mutable values across renders without causing re-renders.
  • Re-rendering:

    • Changes to values managed by useState will trigger a re-render of the component.
    • Changes to values managed by useRef will not trigger a re-render.
  • Usage:

    • Use useState for values that should trigger a re-render when they change (e.g., form inputs, toggles, dynamic data).
    • Use useRef for values that should persist across renders without causing a re-render (e.g., DOM references, interval IDs, previous state values).

Tips and Tricks for these Hooks

  • When to Use useState Over useRef:

    • Use useState when you need the UI to update in response to changes in your data.
    • Use useRef when you need to keep track of a value that doesn’t affect the UI or should not cause a re-render.
  • Combining useState and useRef:

    • In some cases, you might use both hooks together. For example, you can use useRef to keep track of a previous value and useState for the current value, enabling comparisons without unnecessary re-renders.
import React, { useState, useRef, useEffect } from 'react';

const Counter = () => {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    // Update the ref with the current count before the component re-renders
    prevCountRef.current = count;
  }, [count]);

  const prevCount = prevCountRef.current;

  return (
    <div>
      <h1>Current Count: {count}</h1>
      <h2>Previous Count: {prevCount}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;

Enter fullscreen mode Exit fullscreen mode

The Counter component uses both useState and useRef to track the current and previous count values. useState manages the current count and triggers re-renders, while useRef stores the previous count value without causing re-renders.

  • Avoid Overusing useRef:

    • While useRef is powerful, it should not be overused. For most state management needs, useState is the appropriate choice. useRef is best for specific scenarios where you.

My HNG Internship Experience

Before I conclude, I'd like to talk a little about my ongoing internship at HNG. I joined the Frontend Track with the aim of becoming familiar with building and developing web applications in a team and networking with like-minded individuals. You can learn more about the HNG Internship here and explore opportunities to hire top talents from HNG here. I would like for you guys to join me on this Journey with HNG.

Conclusion

Understanding the difference between useState() and useRef() hooks is crucial for building efficient and effective React applications. These hooks serve different purposes and knowing when to use each can greatly enhance your development process.

React Jokes to spice things up.

Why did the React developer break up with his partner?
Because she had too many state issues!

You use useRef, useState but you never useBrain 😄.

Top comments (0)