DEV Community

Cover image for Understanding React’s Core Hooks: useState & useEffect
Hariharan S J
Hariharan S J

Posted on

Understanding React’s Core Hooks: useState & useEffect

1.Introduction

React has become one of the most popular JavaScript libraries for building modern user interfaces. But what truly changed the way developers write React applications was the introduction of Hooks in React 16.8.

Before Hooks, developers mainly relied on class components to manage state, lifecycle methods, and side effects. This often made applications harder to understand and maintain. React Hooks solved this problem by allowing developers to use state and other React features directly inside functional components.

Hooks make React code:

  • Cleaner

  • More readable

  • Easier to reuse

  • Simpler to maintain

Today, almost every modern React application heavily depends on Hooks for handling state management, API calls, performance optimization, and reusable logic.

From simple hooks like useState and useEffect to advanced hooks like useReducer and useMemo, each hook solves a specific problem and helps developers build better applications more efficiently.

In this blog, we’ll explore the most important React Hooks every developer should know, understand how they work, and learn where they are commonly used in real-world projects.

2.What is useState in React?

useState is one of the most important and commonly used Hooks in React. It allows functional components to store, manage, and update data that can change over time.

In simple terms, useState helps React “remember” values and update the UI whenever those values change.

For example, imagine a counter app:

  • Count starts at 0

  • When you click a button, the value increases

  • The UI updates automatically

Without useState, React would not know when to update the screen.

Why Do We Need useState?

Normally, variables in JavaScript do not persist between renders.

For example:

let count = 0;

function increase() {
  count++;
  console.log(count);
}
Enter fullscreen mode Exit fullscreen mode

Even though count changes, React will not automatically update the UI because normal variables are not reactive.

This is where useState comes in.

useState tells React:

“This value can change, and whenever it changes, update the UI.”

Syntax of useState

const [state, setState] = useState(initialValue);
Enter fullscreen mode Exit fullscreen mode

Let’s break this down:

const [count, setCount] = useState(0);
Enter fullscreen mode Exit fullscreen mode

Here:

  • count → Current state value

  • setCount → Function used to update the state

  • 0 → Initial value of the state

Think of it like this:

count → current value
setCount → updates value
useState(0) → starting value
Enter fullscreen mode Exit fullscreen mode

How useState Works

When state changes, React automatically re-renders the component.

Example:

import { useState } from "react";

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

  return (
    <div>
      <h1>{count}</h1>

      <button onClick={() => setCount(count + 1)}>
        Increase
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

How this works:

  1. Initial value starts at 0

  2. User clicks the button

  3. setCount(count + 1) runs

  4. React updates the state

  5. Component re-renders

  6. UI updates automatically

So if count becomes 1, React updates the screen instantly.

Understanding State in Simple Words

You can think of state like a memory box inside a component.

Example:

Counter Component
┌───────────┐
│ count = 0 │
└───────────┘
Enter fullscreen mode Exit fullscreen mode

When the button is clicked:

Counter Component
┌───────────┐
│ count = 1 │
└───────────┘
Enter fullscreen mode Exit fullscreen mode

React notices the change and updates the UI.

Updating State

You update state using the setter function.

Example:

setCount(10);
Enter fullscreen mode Exit fullscreen mode

This changes the value to 10.

You can also update based on the previous value:

setCount(prev => prev + 1);
Enter fullscreen mode Exit fullscreen mode

This is called a functional update.

This approach is useful when the new state depends on the old state.

Example:

setCount(prev => prev + 1);
setCount(prev => prev + 1);
Enter fullscreen mode Exit fullscreen mode

This ensures React updates correctly.

Real-World Use Cases of useState

  • Counter apps

  • Form handling

  • Toggle buttons

  • Dark/light theme

  • Search input values

  • Shopping cart quantity

  • Modal open/close

3.What is useEffect in React?

useEffect is one of the most powerful Hooks in React. It is used to perform side effects inside functional components.

In simple terms, useEffect allows React components to do something after rendering.

This could include:

  • Fetching data from an API

  • Updating the document title

  • Setting timers

  • Adding event listeners

  • Accessing browser APIs

  • Cleaning up resources

Before Hooks existed, these operations were handled using lifecycle methods in class components like componentDidMount and componentDidUpdate.

But with useEffect, React made this process much simpler and cleaner.

What is a Side Effect?

A side effect is anything that affects something outside the component itself.

Examples:

  • Calling an API

  • Changing the webpage title

  • Starting a timer

  • Listening to keyboard or mouse events

  • Updating local storage

These operations happen outside the normal rendering process.

That’s why React provides useEffect.

Syntax of useEffect

useEffect(() => {
  // side effect code
}, []);
Enter fullscreen mode Exit fullscreen mode

Let’s break this down:

useEffect(() => {
  console.log("Component rendered");
}, []);
Enter fullscreen mode Exit fullscreen mode

Here:

  • useEffect() → React Hook

  • First argument → Function containing side effect code

  • Second argument → Dependency array

How useEffect Works

React first renders the component.

After rendering is completed, React runs the code inside useEffect.

Think of it like this:

1. Component renders
2. UI appears on screen
3. useEffect runs
Enter fullscreen mode Exit fullscreen mode

This is why useEffect is called a side-effect Hook.

Example: Updating the Document Title

import { useState, useEffect } from "react";

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

  useEffect(() => {
    document.title = `Count: ${count}`;
  });

  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

What happens here?

  1. Component renders

  2. useEffect runs

  3. Browser tab title updates

  4. Every time count changes, effect runs again

Understanding the Dependency Array

The dependency array controls when the effect should run.

This is one of the most important concepts in useEffect.

1. No Dependency Array

useEffect(() => {
  console.log("Runs after every render");
});
Enter fullscreen mode Exit fullscreen mode

Effect runs:

  • After first render

  • After every re-render

2. Empty Dependency Array

useEffect(() => {
  console.log("Runs only once");
}, []);
Enter fullscreen mode Exit fullscreen mode

Effect runs:

  • Only once after initial render

This behaves similar to:

componentDidMount()
Enter fullscreen mode Exit fullscreen mode

Common use cases:

  • API calls

  • Initial setup

  • Fetching data

3. Dependency Array with Values

useEffect(() => {
  console.log("Runs when count changes");
}, [count]);
Enter fullscreen mode Exit fullscreen mode

Effect runs:

  • Initial render

  • Whenever count changes

React watches the dependency value.

If the value changes, React reruns the effect.

Fetching Data with useEffect

One of the most common use cases.

Example:

import { useEffect, useState } from "react";

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(res => res.json())
      .then(data => setUsers(data));
  }, []);

  return (
    <div>
      {users.map(user => (
        <p key={user.id}>{user.name}</p>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

What happens here?

  • Component loads

  • useEffect runs once

  • API request happens

  • State updates

  • UI re-renders with data

Cleanup Function in useEffect

Sometimes effects create resources that need cleanup.

Examples:

  • Timers

  • Event listeners

  • Subscriptions

React provides a cleanup function for this.

Syntax:

useEffect(() => {
  // setup code

  return () => {
    // cleanup code
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases of useEffect

You’ll use useEffect for:

  • API calls

  • Authentication checks

  • Timers and intervals

  • Event listeners

  • Local storage

  • Updating document title

  • Real-time data fetching

  • Socket connections

4.Final Takeaway

React Hooks completely changed the way developers build React applications. Among all Hooks, useState and useEffect are the foundation of modern React development.

useState helps components store and manage dynamic data, while useEffect allows components to interact with the outside world through side effects like API calls, timers, and event listeners.

Together, these two Hooks make React applications:

  • More dynamic

  • More interactive

  • Easier to maintain

  • Cleaner to write

Top comments (0)