How to Use React Hooks

Introduction React Hooks revolutionized the way developers build components in React by allowing functional components to manage state and side effects without writing class components. Introduced in React 16.8, Hooks provide a more intuitive and concise method to handle component logic, improving code readability and reuse. Understanding how to use React Hooks effectively is essential for modern

Nov 17, 2025 - 11:08
Nov 17, 2025 - 11:08
 0

Introduction

React Hooks revolutionized the way developers build components in React by allowing functional components to manage state and side effects without writing class components. Introduced in React 16.8, Hooks provide a more intuitive and concise method to handle component logic, improving code readability and reuse. Understanding how to use React Hooks effectively is essential for modern React development, enhancing both developer productivity and application performance.

This comprehensive tutorial will guide you through the concepts, practical steps, best practices, tools, and real-world examples to master React Hooks. Whether you are a beginner or looking to deepen your knowledge, this guide will help you leverage Hooks to build clean, efficient, and maintainable React applications.

Step-by-Step Guide

1. Understanding React Hooks

React Hooks are special functions that let you "hook into" React state and lifecycle features from function components. Before Hooks, class components were necessary to handle state and lifecycle methods, but Hooks enable these features directly within functional components.

2. Basic Hooks Overview

The most commonly used built-in Hooks include:

  • useState: Allows you to add state to functional components.
  • useEffect: Performs side effects in function components, such as data fetching or subscriptions.
  • useContext: Accesses React context without using a Consumer component.
  • useReducer: An alternative to useState, useful for complex state logic.
  • useRef: Accesses mutable values that persist across renders, often used for DOM references.

3. How to Use useState

The useState Hook lets you add state variables to your function component.

import React, { useState } from 'react';

function Counter() {

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

return (

<div>

<p>You clicked {count} times</p>

<button onClick={() => setCount(count + 1)}>Click me</button>

</div>

);

}

In this example, count is the state variable, and setCount is the function to update it. Calling setCount re-renders the component with the updated state.

4. How to Use useEffect

The useEffect Hook lets you perform side effects in function components, such as fetching data, setting up subscriptions, or manually changing the DOM.

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

function DataFetcher() {

const [data, setData] = useState(null);

useEffect(() => {

fetch('https://api.example.com/data')

.then(response => response.json())

.then(json => setData(json));

}, []); // Empty dependency array means this runs once on mount

return (

<div>

{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}

</div>

);

}

The second argument to useEffect is the dependency array. It controls when the effect runs. An empty array means the effect runs once after the initial render.

5. Using useContext

The useContext Hook lets you access context values directly in functional components, making it easier to share data across the component tree.

import React, { useContext } from 'react';

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

function ThemedButton() {

const theme = useContext(ThemeContext);

return <button className={theme}>I am styled by theme context!</button>;

}

This avoids the need to wrap components in ThemeContext.Consumer.

6. Advanced Hooks: useReducer and useRef

useReducer is useful when you have complex state logic involving multiple sub-values or when the next state depends on the previous one.

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:

throw new Error();

}

}

function Counter() {

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

return (

<div>

<p>Count: {state.count}</p>

<button onClick={() => dispatch({ type: 'increment' })}>Increment</button>

<button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>

</div>

);

}

useRef is used for persisting a mutable value that does not cause re-renders when updated or for accessing DOM elements directly.

import React, { useRef } from 'react';

function TextInputWithFocusButton() {

const inputEl = useRef(null);

const onButtonClick = () => {

inputEl.current.focus();

};

return (

<div>

<input ref={inputEl} type="text" />

<button onClick={onButtonClick}>Focus the input</button>

</div>

);

}

7. Rules of Hooks

When using Hooks, follow the rules to avoid bugs and ensure consistent behavior:

  • Only call Hooks at the top level of your React function. Don’t call Hooks inside loops, conditions, or nested functions.
  • Only call Hooks from React function components or custom Hooks.

Best Practices

1. Keep Components Small and Focused

Use Hooks to split logic into reusable functions (custom Hooks), keeping components clean and focused on rendering UI.

2. Use Custom Hooks for Reusable Logic

Extract common stateful logic to custom Hooks to promote code reuse and readability.

import { useState, useEffect } from 'react';

function useWindowWidth() {

const [width, setWidth] = useState(window.innerWidth);

useEffect(() => {

const handleResize = () => setWidth(window.innerWidth);

window.addEventListener('resize', handleResize);

return () => window.removeEventListener('resize', handleResize);

}, []);

return width;

}

3. Manage Dependencies Carefully in useEffect

Always specify dependencies in useEffect to avoid unwanted effects or infinite loops. Use ESLint plugin for React Hooks to identify missing dependencies.

4. Avoid Overusing useState

For complex state logic, prefer useReducer to manage state transitions explicitly.

5. Use useCallback and useMemo When Needed

Optimize performance by memoizing functions with useCallback and computed values with useMemo to prevent unnecessary re-renders.

6. Handle Side Effects Properly

Clean up side effects inside useEffect by returning a cleanup function to avoid memory leaks, especially for subscriptions or timers.

Tools and Resources

1. React DevTools

React DevTools browser extension lets you inspect React component trees and Hooks state in real-time, crucial for debugging.

2. ESLint Plugin for React Hooks

eslint-plugin-react-hooks enforces the Rules of Hooks and helps catch common mistakes early.

3. React Official Documentation

The React Hooks documentation is the authoritative resource for understanding Hooks in depth.

4. CodeSandbox and StackBlitz

Online playgrounds like CodeSandbox and StackBlitz provide instant environments to experiment with Hooks and share code snippets.

5. Tutorials and Courses

Platforms like Udemy, Coursera, and freeCodeCamp offer in-depth courses and tutorials focused on React Hooks to enhance practical skills.

Real Examples

1. Fetching Data with useEffect and useState

This example shows how to fetch data from an API when a component mounts.

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

function UserList() {

const [users, setUsers] = useState([]);

const [loading, setLoading] = useState(true);

useEffect(() => {

fetch('https://jsonplaceholder.typicode.com/users')

.then(response => response.json())

.then(data => {

setUsers(data);

setLoading(false);

});

}, []);

if (loading) return <p>Loading users...</p>;

return (

<ul>

{users.map(user => (

<li key={user.id}>{user.name}</li>

))}

</ul>

);

}

2. Form Handling with useState

Managing form inputs using useState Hook.

import React, { useState } from 'react';

function SignupForm() {

const [formData, setFormData] = useState({

username: '',

email: '',

});

const handleChange = (e) => {

const { name, value } = e.target;

setFormData(prevState => ({

...prevState,

[name]: value,

}));

};

const handleSubmit = (e) => {

e.preventDefault();

alert(Username: ${formData.username}, Email: ${formData.email});

};

return (

<form onSubmit={handleSubmit}>

<input

type="text"

name="username"

value={formData.username}

onChange={handleChange}

placeholder="Username"

/>

<input

type="email"

name="email"

value={formData.email}

onChange={handleChange}

placeholder="Email"

/>

<button type="submit">Sign Up</button>

</form>

);

}

3. Using useReducer for Complex State

Example of a todo list using useReducer to manage multiple actions.

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

const initialTodos = [];

function reducer(state, action) {

switch (action.type) {

case 'add':

return [...state, { id: Date.now(), text: action.text, completed: false }];

case 'toggle':

return state.map(todo =>

todo.id === action.id ? { ...todo, completed: !todo.completed } : todo

);

case 'remove':

return state.filter(todo => todo.id !== action.id);

default:

return state;

}

}

function TodoApp() {

const [todos, dispatch] = useReducer(reducer, initialTodos);

const [text, setText] = useState('');

const handleSubmit = e => {

e.preventDefault();

if (text.trim()) {

dispatch({ type: 'add', text });

setText('');

}

};

return (

<div>

<form onSubmit={handleSubmit}>

<input

value={text}

onChange={e => setText(e.target.value)}

placeholder="Add todo"

/>

<button type="submit">Add</button>

</form>

<ul>

{todos.map(todo => (

<li key={todo.id}>

<label>

<input

type="checkbox"

checked={todo.completed}

onChange={() => dispatch({ type: 'toggle', id: todo.id })}

/>

{todo.text}

</label>

<button onClick={() => dispatch({ type: 'remove', id: todo.id })}>Delete</button>

</li>

))}

</ul>

</div>

);

}

FAQs

What are React Hooks?

React Hooks are functions that let you use React state and lifecycle features in functional components without writing class components.

Can I use Hooks in class components?

No. Hooks are designed to work only with functional components.

Why should I use Hooks instead of class components?

Hooks simplify component logic, improve code reuse, and avoid complexities related to the this keyword in classes.

What is the difference between useState and useReducer?

useState is best for simple state updates, while useReducer is suited for complex state logic involving multiple state transitions.

How do I avoid infinite loops with useEffect?

Make sure to specify all necessary dependencies in the dependency array and avoid mutating dependencies inside the effect.

Can I create my own custom Hooks?

Yes, custom Hooks let you extract reusable logic from components. They follow the naming convention starting with "use".

Conclusion

React Hooks have transformed how developers build React applications by enabling state and side effect management within functional components. Mastering Hooks like useState, useEffect, useContext, and useReducer is essential for writing modern, efficient, and maintainable React code.

By following best practices, leveraging available tools, and practicing with real examples, you can enhance your React development skills significantly. Embrace React Hooks to build cleaner, more scalable components and improve your overall application architecture.