Learning React Hooks

React Hooks are a game-changer in the React ecosystem, enabling functional components to manage state and handle side effects without needing to convert them into class components. Introduced in React 16.8, Hooks have become a core part of React development and are now considered the standard approach to state management and lifecycle methods in modern React applications.
What are React Hooks?
React Hooks are functions that let you "hook into" React's state and lifecycle features in functional components. They allow you to leverage the power of React’s internal system without writing class components.
Commonly Used Hooks:
- useState: Manage state within a component.
- useEffect: Handle side effects such as data fetching, subscriptions, or manually changing the DOM.
- useContext: Access the React context without needing to wrap components in a Consumer.
- useReducer: An alternative to
useStatefor managing complex state logic. - useRef: Directly access DOM elements or persist values between renders.
In this article, we’ll dive into the most widely used hooks and how to incorporate them into your React apps.
useState – State Management in Functional Components
useState is the most commonly used hook, allowing you to add local state to your function components. Before hooks, this functionality was only available in class components.
Here’s a simple example of using useState to manage a counter:
import { 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:
useState(0)initializes the state with a value of0.- The
countvariable holds the current state value, andsetCountis the function used to update it. - When the button is clicked,
setCountincrements the count value by 1.
useEffect – Managing Side Effects
useEffect allows you to perform side effects in your function components. Side effects include things like fetching data from an API, setting up subscriptions, or manually changing the DOM.
Here’s how you might use useEffect to fetch data from an API:
import { useState, useEffect } 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));
}, [userId]);
if (!user) {
return <p>Loading user data...</p>;
}
return (
<div>
<h1>{user.name}</h1>
<p>{user.bio}</p>
</div>
);
}
In this example:
useEffecttakes two arguments: a callback function and an array of dependencies.- The effect runs whenever any of the dependencies in the array change (
userIdin this case). - The component fetches user data when it mounts and when
userIdchanges.
useContext – Simplifying Context Consumption
React's Context API allows you to pass data through the component tree without having to pass props manually at every level. The useContext hook provides a simpler way to consume context in functional components.
Here’s an example of using useContext:
import { useContext } from "react";
import { ThemeContext } from "./ThemeContext";
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.color }}>
I am styled by theme context!
</button>
);
}
With useContext, you no longer need to use <ThemeContext.Consumer> to access the context value, making your components more readable.
useReducer – Managing Complex State
For more complex state logic, or when the state changes are dependent on previous state values, useReducer provides a more robust alternative to useState. It works similarly to Redux, where you define a reducer function that specifies how the state should change based on dispatched actions.
Here’s an example:
import { 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>
);
}
In this example:
- The
reducerfunction takes the current state and an action, then returns a new state based on the action type. useReducerreturns the current state and adispatchfunction to trigger state updates.
useRef – Accessing DOM Elements and Persisting Values
useRef is used to persist values across renders or to directly access a DOM element. Unlike state, changes to a ref do not trigger a re-render.
Here’s an example of using useRef to focus on an input element:
import { useRef } from "react";
function TextInput() {
const inputRef = useRef();
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}
In this example:
- The
useRefhook creates a reference to the input element. - The
focusInputfunction callsinputRef.current.focus()to focus the input element.
Custom Hooks – Building Reusable Logic
One of the most powerful features of React Hooks is the ability to create your own custom hooks. Custom hooks allow you to extract and reuse logic across multiple components.
Here’s an example of a custom hook that handles form input:
import { useState } from "react";
function useInput(initialValue) {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return [value, handleChange];
}
You can now use this custom hook in any component:
function Form() {
const [name, setName] = useInput("");
return (
<form>
<input value={name} onChange={setName} />
<p>Your name is {name}</p>
</form>
);
}
In this example, the custom useInput hook manages the input state, making the form logic reusable across multiple components.
Conclusion
React Hooks offer a more functional and concise way to manage state and lifecycle methods in React components. From basic state management with useState to handling complex side effects with useEffect and even creating custom hooks, they provide a modern approach to building powerful React applications.
Whether you're developing small-scale projects or large, production-ready applications, understanding and utilizing React Hooks can significantly improve your development workflow.

In summary, React Hooks allow for:
- Cleaner code with functional components.
- State and side effects management without class components.
- The ability to reuse logic with custom hooks, improving code maintainability.
This version introduces React Hooks, explaining key concepts like useState, useEffect, and useContext, with code examples and explanations for each.