You are currently viewing What Are React Hooks? A Beginner’s Guide
React Hooks

What Are React Hooks? A Beginner’s Guide

React Hooks have revolutionized the way developers build React applications by enabling functional components to manage state and side effects. Introduced in React 16.8, Hooks eliminate the need for class components, making code cleaner and easier to understand.

If you’re new to React or transitioning from class components to functional components, this guide will help you understand Hooks from the ground up.

React Event Handling Best Practices

Table of Contents

  1. Introduction to React Hooks
  2. Why Were Hooks Introduced?
  3. Rules of Using Hooks
  4. Built-in React Hooks
    • useState
    • useEffect
    • useContext
    • useReducer
    • useRef
    • useMemo
    • useCallback
    • useLayoutEffect
  5. Custom Hooks
  6. Best Practices for Using Hooks
  7. Conclusion

1. Introduction to React Hooks

React Hooks allow developers to use state and lifecycle features in functional components. Before Hooks, only class components could manage state and lifecycle methods, making functional components less powerful.

With Hooks, we can now:

  • Manage local state using useState
  • Perform side effects using useEffect
  • Use context without wrapping components in Consumer
  • Optimize performance with useMemo and useCallback
  • Create custom reusable logic with custom Hooks

2. Why Were Hooks Introduced?

React Hooks were introduced to solve the following issues:

1. Complex Class Components

Class components require a lot of boilerplate code and often become hard to manage as the application grows.

class Counter extends React.Component {
    constructor(props) {
        super(props);
        this.state = { count: 0 };
    }

    increment = () => {
        this.setState({ count: this.state.count + 1 });
    };

    render() {
        return <button onClick={this.increment}>Count: {this.state.count}</button>;
    }
}

2. Reusing Logic Was Difficult

Before Hooks, reusing logic across multiple components required higher-order components (HOCs) or render props, which could lead to complex and unreadable code.

3. Lifecycles Were Hard to Manage

In class components, lifecycle methods such as componentDidMount and componentDidUpdate were often combined into a single function, leading to tangled logic.

Hooks solve these problems by making state management and side effects simpler and more reusable.


3. Rules of Using Hooks

There are two fundamental rules when using Hooks:

  1. Hooks must be called at the top level of a function.
    • They should not be inside loops, conditions, or nested functions.
  2. Hooks must be used inside React functional components or custom Hooks.
    • They cannot be used in regular JavaScript functions.

4. Built-in React Hooks

4.1 useState: Managing State in Functional Components

The useState Hook allows us to manage state in a functional component.

import React, { useState } from "react";

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

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

export default Counter;

Each call to useState returns an array with the current state value and a function to update it.


4.2 useEffect: Handling Side Effects

The useEffect Hook is used for side effects such as fetching data, setting up event listeners, and interacting with the DOM.

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

function Timer() {
    const [seconds, setSeconds] = useState(0);

    useEffect(() => {
        const interval = setInterval(() => {
            setSeconds((prev) => prev + 1);
        }, 1000);

        return () => clearInterval(interval); // Cleanup function
    }, []);

    return <p>Timer: {seconds} seconds</p>;
}
  • The empty dependency array ([]) ensures the effect runs only once.
  • The cleanup function prevents memory leaks.

4.3 useContext: Simplifying Context API

The useContext Hook provides access to context values without needing Consumer wrappers.

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

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

function Toolbar() {
    const theme = useContext(ThemeContext);
    return <p>Current Theme: {theme}</p>;
}

4.4 useReducer: Managing Complex State

The useReducer Hook is useful for managing complex state logic.

import React, { useReducer } from "react";

const initialState = { count: 0 };

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, initialState);

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

4.5 useRef: Accessing DOM Elements and Persisting Values

The useRef Hook is used to reference DOM elements or persist values without triggering re-renders.

import React, { useRef } from "react";

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

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

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

4.6 useMemo: Optimizing Performance

The useMemo Hook memoizes expensive calculations to improve performance.

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

function ExpensiveComponent({ number }) {
    const computedValue = useMemo(() => {
        console.log("Computing...");
        return number * 2;
    }, [number]);

    return <p>Computed Value: {computedValue}</p>;
}

4.7 useCallback: Preventing Unnecessary Re-Renders

The useCallback Hook memoizes functions to prevent unnecessary re-creations.

const memoizedCallback = useCallback(() => {
    doSomething(a, b);
}, [a, b]);

5. Custom Hooks: Creating Reusable Logic

Custom Hooks allow you to extract reusable logic from components.

function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
        return localStorage.getItem(key) || initialValue;
    });

    useEffect(() => {
        localStorage.setItem(key, value);
    }, [key, value]);

    return [value, setValue];
}

6. Best Practices for Using Hooks

  1. Use multiple Hooks instead of one large useEffect.
  2. Keep state minimal and avoid unnecessary updates.
  3. Use useMemo and useCallback for performance optimizations.
  4. Always clean up side effects in useEffect.
  5. Use custom Hooks to keep code DRY (Don’t Repeat Yourself).

7. Conclusion

React Hooks simplify state management, side effects, and component logic. By mastering Hooks like useState, useEffect, useContext, and useReducer, you can write cleaner and more efficient React applications.

By following best practices and using Hooks effectively, you can build scalable React applications with ease.