How to Use React Hooks

Introduction React Hooks have revolutionized the way developers build functional components in React. Introduced in React 16.8, Hooks provide a powerful yet straightforward approach to managing state, side effects, and lifecycle events without the need for class components. Understanding how to use React Hooks effectively is essential for modern React development, enabling cleaner, more reusable,

Nov 17, 2025 - 11:28
Nov 17, 2025 - 11:28
 3

Introduction

React Hooks have revolutionized the way developers build functional components in React. Introduced in React 16.8, Hooks provide a powerful yet straightforward approach to managing state, side effects, and lifecycle events without the need for class components. Understanding how to use React Hooks effectively is essential for modern React development, enabling cleaner, more reusable, and maintainable code.

This tutorial will provide a comprehensive guide on how to use React Hooks, covering fundamental concepts, step-by-step instructions, best practices, useful tools, and real-world examples. Whether you are a beginner or an experienced developer looking to deepen your knowledge, this guide will equip you with the skills to harness the full potential of React Hooks.

Step-by-Step Guide

1. Setting Up Your React Environment

Before diving into Hooks, ensure you have a React environment ready. You can create a new React app using Create React App, which comes pre-configured for Hooks support.

Run the following command in your terminal:

npx create-react-app react-hooks-tutorial

Once the setup is complete, navigate to your project directory and start the development server:

cd react-hooks-tutorial
npm start

2. Understanding the Basics of React Hooks

React provides several built-in Hooks. The most commonly used ones include:

  • useState: Manages state in functional components.
  • useEffect: Handles side effects such as data fetching and subscriptions.
  • useContext: Accesses React context without nesting.
  • useRef: References DOM elements or stores mutable values.
  • useReducer: Manages complex state logic.

3. Using useState

The useState Hook allows you to add state to functional components. Here's how to use it:

Import useState from React:

import React, { useState } from 'react';

Example of a counter component:

function Counter() {

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

return (

<div>

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

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

</div>

);

}

Explanation:

  • count holds the current state value.
  • setCount is a function to update the state.
  • The argument 0 sets the initial state.

4. Using useEffect

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

Example of fetching data when a component mounts:

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 ensures this runs once on mount

if (!data) return <p>Loading...</p>;

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

}

Key points:

  • The effect runs after the first render because of the empty dependency array [].
  • It fetches data asynchronously and updates the state.

5. Using useContext

useContext simplifies consuming context values without the need for a Context.Consumer wrapper.

Example:

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

const ThemeContext = createContext('light');

function ThemedButton() {

const theme = useContext(ThemeContext);

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

}

function App() {

return (

<ThemeContext.Provider value="dark">

<ThemedButton />

</ThemeContext.Provider>

);

}

6. Using useRef

useRef allows you to persist values between renders or access DOM elements directly.

Example for focusing an input:

import React, { useRef } from 'react';

function FocusInput() {

const inputRef = useRef(null);

const focus = () => {

inputRef.current.focus();

};

return (

<div>

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

<button onClick={focus}>Focus input</button>

</div>

);

}

7. Using useReducer

useReducer is an alternative to useState for more complex state logic, similar to Redux reducers.

Example:

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' })}>+</button>

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

</div>

);

}

Best Practices

1. Use Hooks Only at the Top Level

Always call Hooks at the top level of your React function. Do not call Hooks inside loops, conditions, or nested functions. This ensures consistent Hook order between renders.

2. Use Hooks Only in React Functions

Call Hooks from React function components or custom Hooks. Avoid calling them in regular JavaScript functions.

3. Keep Effects Clean

Return a cleanup function from useEffect when necessary to avoid memory leaks, especially with subscriptions or timers.

4. Use Dependency Arrays Wisely

Specify all dependencies in the useEffect dependency array to prevent stale closures or unintended behaviors.

5. Create Custom Hooks for Reusability

Extract reusable logic into custom Hooks. This promotes cleaner code and better separation of concerns.

6. Avoid Overusing useReducer

Use useReducer when state logic is complex. For simple state, prefer useState to keep components lightweight.

7. Optimize Performance

Use useMemo and useCallback Hooks to memoize expensive calculations and functions to avoid unnecessary re-renders.

Tools and Resources

1. React DevTools

A browser extension that helps inspect React component hierarchies, state, and Hooks. Essential for debugging and understanding your Hooks usage.

2. ESLint Plugin for React Hooks

Use the eslint-plugin-react-hooks to enforce the Rules of Hooks and catch common mistakes during development.

3. Official React Documentation

The React docs provide thorough explanations and examples for all Hooks: https://reactjs.org/docs/hooks-intro.html

4. CodeSandbox

An online code editor perfect for experimenting with React Hooks without setup overhead: https://codesandbox.io

5. React Hook Form

A popular library that leverages Hooks to simplify form management in React: https://react-hook-form.com

Real Examples

Example 1: Building a Todo List with useState and useEffect

This example demonstrates managing a list of todos with state and persisting them in localStorage.

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

function TodoApp() {

const [todos, setTodos] = useState(() => {

const savedTodos = localStorage.getItem('todos');

return savedTodos ? JSON.parse(savedTodos) : [];

});

const [input, setInput] = useState('');

useEffect(() => {

localStorage.setItem('todos', JSON.stringify(todos));

}, [todos]);

const addTodo = () => {

if (!input.trim()) return;

setTodos([...todos, { id: Date.now(), text: input, completed: false }]);

setInput('');

};

const toggleTodo = id => {

setTodos(todos.map(todo =>

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

));

};

return (

<div>

<h2>Todo List</h2>

<input

type="text"

value={input}

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

placeholder="Add todo"

/>

<button onClick={addTodo}>Add</button>

<ul>

{todos.map(todo => (

<li

key={todo.id}

onClick={() => toggleTodo(todo.id)}

style={{ textDecoration: todo.completed ? 'line-through' : 'none', cursor: 'pointer' }}

>

{todo.text}

</li>

))}

</ul>

</div>

);

}

Example 2: Custom Hook for Window Width

This custom Hook tracks the browser window width and updates the component accordingly.

import React, { 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;

}

function ResponsiveComponent() {

const width = useWindowWidth();

return <div>Window width is {width}px</div>;

}

FAQs

Q1: Can I use Hooks inside class components?

No. Hooks are designed to work only with functional components. Class components require different patterns for managing state and lifecycle events.

Q2: What is the difference between useState and useReducer?

useState is suitable for simple state updates, while useReducer is better for complex state logic involving multiple sub-values or when the next state depends on the previous one.

Q3: How do I avoid infinite loops with useEffect?

Carefully specify the dependency array for useEffect. Avoid changing dependencies inside the effect or ensure the effect cleanup prevents repeated triggering.

Q4: Can I create my own custom Hooks?

Yes. Custom Hooks are a powerful way to extract and reuse stateful logic across components, following the naming convention of starting with "use".

Q5: Are Hooks backwards compatible?

Hooks were introduced in React 16.8; they do not work with earlier versions. For older codebases, consider upgrading React before adopting Hooks.

Conclusion

React Hooks have become indispensable for writing modern, efficient, and maintainable React applications. By mastering core Hooks such as useState, useEffect, and useContext, along with best practices and custom Hook creation, developers can dramatically improve their workflow and code quality.

This tutorial outlined the fundamental steps to get started with Hooks, discussed practical considerations, introduced useful tools, and shared real-world examples to inspire your React projects. Embrace React Hooks today to unlock the full potential of functional programming in React.