DEV Community

Vishal Yadav
Vishal Yadav

Posted on

πŸš€ Most Useful React Hooks for Your Next Project πŸš€

React hooks have revolutionized how we write components in React, making our code more readable, maintainable, and functional. Whether you're a seasoned developer or just starting out, these hooks are essential tools that can significantly improve your projects. In this blog, we’ll explore some of the most useful React hooks, along with examples of how to use them effectively.

βš›οΈ 1. useState - Manage State in Functional Components

What It Does:
useState is the go-to hook for managing state in functional components. It allows you to add state to your components, which can then be updated over time.

Why It's Important:
State management is crucial for creating dynamic, interactive components. useState lets you store and update values that need to change, like form inputs or user interactions.

Example in Practice:
Imagine a simple form where users can type their name. You need to store that name somewhere, and useState provides the perfect solution:

import React, { useState } from 'react';

function NameForm() {
  const [name, setName] = useState('');

  const handleChange = (event) => {
    setName(event.target.value);
  };

  return (
    <div>
      <input type="text" value={name} onChange={handleChange} />
      <p>Your name is: {name}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, the useState hook stores the name input, and setName updates it as the user types.

πŸ”„ 2. useEffect - Handle Side Effects

What It Does:
useEffect allows you to perform side effects in your components, such as data fetching, directly interacting with the DOM, or setting up subscriptions.

Why It's Important:
Side effects are essential for making your app dynamic. Without useEffect, your functional components would have no way to perform tasks that aren't purely about rendering.

Example in Practice:
Fetching user data when a component loads is a common task:

import React, { useEffect, useState } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`https://api.example.com/user/${userId}`)
      .then((response) => response.json())
      .then((data) => setUser(data));

    return () => console.log('Cleanup if needed');
  }, [userId]);

  return (
    <div>
      {user ? <p>User name: {user.name}</p> : <p>Loading...</p>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, useEffect fetches data when the component mounts and updates whenever the userId changes.

πŸ“œ 3. useContext - Simplify State Sharing

What It Does:
useContext allows you to access shared state across your components without manually passing props through each level of the component tree.

Why It's Important:
It eliminates the need for prop drilling, making your code cleaner and easier to manage, especially in larger applications.

Example in Practice:
Consider a theme setting that multiple components need to access:

import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={`btn-${theme}`}>Themed Button</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, useContext allows the ThemedButton to access the theme directly, without passing it through props.

πŸš€ 4. useReducer - Manage Complex State

What It Does:
useReducer is used for managing complex state logic by dispatching actions to a reducer function. It offers more control than useState, making it ideal for complex state transitions.

Why It's Important:
When your state logic becomes too complex for useState, useReducer can help you manage it more effectively, particularly when the next state depends on the previous one.

Example in Practice:
Managing a shopping cart with actions like adding or removing items:

import React, { useReducer } from 'react';

const initialState = { cart: [] };

function reducer(state, action) {
  switch (action.type) {
    case 'add':
      return { cart: [...state.cart, action.item] };
    case 'remove':
      return { cart: state.cart.filter((item) => item.id !== action.id) };
    default:
      return state;
  }
}

function ShoppingCart() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <ul>
        {state.cart.map((item) => (
          <li key={item.id}>
            {item.name}
            <button onClick={() => dispatch({ type: 'remove', id: item.id })}>
              Remove
            </button>
          </li>
        ))}
      </ul>
      <button
        onClick={() =>
          dispatch({ type: 'add', item: { id: 1, name: 'New Item' } })
        }
      >
        Add Item
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, useReducer handles the state transitions for adding and removing items from the cart.

πŸ” 5. useMemo - Optimize Performance

What It Does:
useMemo is used to memoize the result of an expensive computation, ensuring that it only recalculates when necessary.

Why It's Important:
In complex or large applications, optimizing performance is critical. useMemo prevents unnecessary recalculations, which can slow down your app.

Example in Practice:
Filtering a list based on a search term:

import React, { useMemo } from 'react';

function FilteredList({ items, filter }) {
  const filteredItems = useMemo(() => {
    console.log('Filtering items');
    return items.filter((item) => item.includes(filter));
  }, [items, filter]);

  return (
    <ul>
      {filteredItems.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, useMemo ensures that the filtering only happens when items or filter changes, avoiding unnecessary computations.

πŸ“ž 6. useCallback - Stabilize Function References

What It Does:
useCallback returns a memoized version of a callback function, which helps prevent unnecessary re-renders of components that rely on that function, especially when passing callbacks as props.

Why It's Important:
Stabilizing function references is crucial when passing functions to child components to avoid re-renders that can degrade performance.

Example in Practice:
Preventing a button from re-rendering unnecessarily:

import React, { useState, useCallback } from 'react';

function Button({ onClick }) {
  console.log('Button rendered');
  return <button onClick={onClick}>Click me</button>;
}

function App() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount((c) => c + 1);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <Button onClick={increment} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, useCallback ensures that the increment function reference remains stable, so the Button component doesn’t re-render unless it needs to.

🧩 7. useRef - Persist Values and Access DOM Elements

What It Does:
useRef gives you a way to persist values across renders without causing a re-render when the value changes. It also provides access to DOM elements directly.

Why It's Important:
Sometimes, you need to maintain a mutable value or directly interact with a DOM element (like focusing an input field), and useRef is the perfect tool for that.

Example in Practice:
Automatically focusing an input field when a component mounts:

import React, { useRef, useEffect } from 'react';

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

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

  return <input ref={inputRef} type="text" />;
}
Enter fullscreen mode Exit fullscreen mode

Here, useRef is used to focus the input field automatically when the component mounts, providing a better user experience.

Conclusion

These React hooks can be game-changers for your next project, making your code cleaner, more efficient, and easier to manage. By understanding when and how to use these hooks, you can take full advantage of React’s capabilities and build better, more performant applications.

Top comments (11)

Collapse
 
manvendrask profile image
Manvendra Singh

It is not a blog and doesn't bring anything new to the table.

Collapse
 
vyan profile image
Vishal Yadav

It is for those who don't know , if you know that's good

Collapse
 
prakirth profile image
Prakirth Govardhanam

I understand that this might be redundant for seniors/experts but it really helps early-stage developers like me to revise what they have been learning.
Thank you for the revision @vyan ! Also adding snippets on latest hooks such as useQuery would be a good update.

Collapse
 
vyan profile image
Vishal Yadav

Yeah Sure

Collapse
 
sjiamnocna profile image
Šimon Janča

Nothing new. Post bout useQuery with server actions would be more helpful.

Collapse
 
vyan profile image
Vishal Yadav

I will

Collapse
 
r8928 profile image
Info Comment hidden by post author - thread only accessible via permalink
r8928

The title is a click bait πŸ˜‚πŸ˜‚

Collapse
 
jayantbh profile image
Jayant Bhawal

Uh... I don't know about most useful. Or even being useful for early stage devs since these are basically the fundamental hooks one needs to know about when learning react.

But I could offer an addition to the list perhaps.

I find useState and using two variables to get and set state to be a bit cumbersome, and the moment I need to do more it's... well, more code.

useEasyState in this blog post is kinda cool. (Disclaimer: I'm associated with the team that made this)

Collapse
 
rmcdeveloper profile image
Ronaldo Correa

Half of these.hooks will vanish soon with React 19..Thank God! Like useMemo and useCallback and useEffect! These three hooks are so stupid.

Collapse
 
suraj__ profile image
Suraj

Nice!

Collapse
 
oyhqstar profile image
oyhqstar

Nothing new

Some comments have been hidden by the post's author - find out more