10 Must-Know React.js Hooks for Modern Web Development

React.js hooks have transformed the way developers build modern web applications. Before React 16.8, managing state and side effects required class components, making the code more complex and harder to maintain. Hooks introduced a cleaner, more functional approach, allowing developers to use state and other React features without writing a class.

2025-02-05 14:08:22 - CodeWithDiv

Introduction

React.js hooks have transformed the way developers build modern web applications. Before React 16.8, managing state and side effects required class components, making the code more complex and harder to maintain. Hooks introduced a cleaner, more functional approach, allowing developers to use state and other React features without writing a class. In this article, we'll explore 10 essential React.js hooks that every modern developer should know. These hooks help manage state, optimize performance, and enhance code reusability, making them indispensable for React development.

1. useState: Managing Component State

The useState hook is one of the most fundamental hooks in React. It allows functional components to manage local state, eliminating the need for class-based state management.

Syntax and Usage Example

import React, { useState } from 'react';


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


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


Best Practices

2. useEffect: Handling Side Effects

The useEffect hook allows developers to handle side effects like API calls, event listeners, and subscriptions inside functional components.

Basic Example

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


function DataFetcher() {
  const [data, setData] = useState([]);


  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []); // Empty dependency array means it runs only once


  return <div>{JSON.stringify(data)}</div>;
}


Best Practices

3. useContext: Simplifying State Management

The useContext hook simplifies state management by allowing components to access shared state without prop drilling.

Example Usage

import React, { useContext, createContext } from 'react';


const ThemeContext = createContext();


function ThemedComponent() {
  const theme = useContext(ThemeContext);
  return <div style={{ background: theme }}>Current Theme: {theme}</div>;
}


function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedComponent />
    </ThemeContext.Provider>
  );
}


Why useContext is Useful

4. useRef: Accessing DOM Elements and Preserving Values

The useRef hook is commonly used for direct DOM manipulation and storing values between renders without triggering re-renders.

Example: Managing Focus

import React, { useRef } from 'react';


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


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


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


Best Practices

5. useReducer: Advanced State Management

The useReducer hook is useful for managing complex state logic, offering a more structured approach than useState.

Basic Example

import React, { useReducer } from 'react';


function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}


function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });


  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </div>
  );
}


When to Use useReducer

6. useMemo: Performance Optimization with Memoization

The useMemo hook helps optimize performance by memoizing expensive computations, preventing unnecessary recalculations during renders.

Example: Optimizing Expensive Calculations

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


function ExpensiveComponent({ num }) {
  const calculateFactorial = (n) => {
    console.log('Calculating...');
    return n <= 1 ? 1 : n * calculateFactorial(n - 1);
  };


  const memoizedValue = useMemo(() => calculateFactorial(num), [num]);


  return <p>Factorial of {num}: {memoizedValue}</p>;
}


export default function App() {
  const [count, setCount] = useState(5);
  return (
    <div>
      <ExpensiveComponent num={count} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}


Best Practices

7. useCallback: Preventing Unnecessary Function Re-Creation

The useCallback hook prevents unnecessary function re-creations by memoizing callback functions, improving performance in components with frequent re-renders.

Example: Memoizing Event Handlers

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


function Button({ handleClick }) {
  return <button onClick={handleClick}>Click Me</button>;
}


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


  const memoizedHandleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);


  return (
    <div>
      <p>Count: {count}</p>
      <Button handleClick={memoizedHandleClick} />
    </div>
  );
}


8. useLayoutEffect: Running Effects Before Painting

useLayoutEffect is similar to useEffect, but it runs synchronously after all DOM mutations and before the browser paints.

Example: Measuring DOM Elements Before Painting

import React, { useState, useRef, useLayoutEffect } from 'react';


function MeasureComponent() {
  const divRef = useRef(null);
  const [height, setHeight] = useState(0);


  useLayoutEffect(() => {
    setHeight(divRef.current.getBoundingClientRect().height);
  }, []);


  return (
    <div>
      <div ref={divRef} style={{ padding: '20px', background: 'lightblue' }}>
        Resize me
      </div>
      <p>Height: {height}px</p>
    </div>
  );
}


When to Use useLayoutEffect

9. useImperativeHandle: Customizing Ref Exposures in Components

useImperativeHandle allows customization of a component’s exposed methods when using forwardRef.

Example: Exposing a Custom Method

import React, { useRef, useImperativeHandle, forwardRef } from 'react';


const CustomInput = forwardRef((props, ref) => {
  const inputRef = useRef();


  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));


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


export default function App() {
  const inputRef = useRef();


  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
}


When to Use useImperativeHandle

10. Custom Hooks: Creating Reusable Logic

Custom hooks allow developers to extract and reuse logic across multiple components, promoting cleaner and more maintainable code.

Example: Creating a useFetch Hook

import { useState, useEffect } from 'react';


function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);


  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      })
      .catch((err) => {
        setError(err);
        setLoading(false);
      });
  }, [url]);


  return { data, loading, error };
}


export default useFetch;


Example Usage in a Component

import React from 'react';
import useFetch from './useFetch';


function UsersList() {
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/users');


  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;


  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}


export default UsersList;


Why Use Custom Hooks?

Conclusion

React hooks have drastically simplified the way developers build modern applications. From basic state management with useState to performance optimization with useMemo and useCallback, hooks offer powerful tools to make React development more efficient. Mastering these 10 essential hooks will help you write cleaner, more maintainable, and optimized React applications. Experiment with them, and start integrating them into your projects for better performance and reusability.

More Posts