Back to Web Development

Module 4: State Management & APIs

Build dynamic applications that manage complex state and fetch data from APIs

🔄 What is State Management?

Imagine your application is like a video game. "State" is all the information about what's happening right now - your score, health, inventory, current level, etc. State management is how you organize, update, and share this information across your entire application!

Simple Definition

State is data that changes over time in your application. It could be user input, fetched data, UI state (is modal open?), or anything that affects what users see. State management is the pattern/tools you use to handle this data efficiently.

Examples of State:

• User logged in or not?

• Shopping cart items

• Form input values

• Data loaded from API

• Dark mode on/off

• Current page/route

Why State Management Matters

Predictable Updates

Know exactly how and when data changes

Share Data Easily

Access same data across multiple components

Easier Debugging

Track state changes and find bugs faster

Better Performance

Update only what needs to change

⚛️ React State Basics

In React, we use hooks like useState to manage component state. Let's understand the fundamentals!

useState Hook

// Simple counter example

import { useState } from 'react';

function Counter() {

// Declare state variable

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

return (

<div>

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

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

Increment

</button>

</div>

);

}

// Form input example

function LoginForm() {

const [email, setEmail] = useState('');

const [password, setPassword] = useState('');

const handleSubmit = (e) => {

e.preventDefault();

console.log(email, password);

};

return (

<form onSubmit={handleSubmit} >

<input

value={email}

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

/>

<input

type="password"

value={password}

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

/>

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

</form>

);

}

💡 State Update Rules:

  • • Never modify state directly: count++ ❌ Use setCount(count + 1) ✅
  • • State updates are asynchronous
  • • Use functional updates when new state depends on old: setCount(prev => prev + 1)
  • • Each setState call triggers a re-render

🌐 Context API - Global State

When you need to share state across many components (like user info, theme, language), passing props through every level gets messy. Context API lets you create "global" state accessible anywhere!

When to Use Context

✅ User authentication state

✅ Theme (dark/light mode)

✅ Language/localization

✅ Shopping cart

❌ Frequently changing data (use state management library instead)

// 1. Create Context

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

const ThemeContext = createContext();

// 2. Create Provider Component

function ThemeProvider({ children }) {

const [theme, setTheme] = useState('light');

const toggleTheme = () => {

setTheme(theme === 'light' ? 'dark' : 'light');

};

return (

<ThemeContext.Provider value={{ theme, toggleTheme }} >

{children}

</ThemeContext.Provider>

);

}

// 3. Use in any component

function Header() {

const { theme, toggleTheme } = useContext(ThemeContext);

return (

<div className={theme} >

<button onClick={toggleTheme} >Toggle Theme</button>

</div>

);

}

🌍 Working with APIs

APIs (Application Programming Interfaces) are how your frontend talks to backends and external services. Think of them as waiters in a restaurant - you tell them what you want, they go to the kitchen (server), and bring back your food (data)!

What is a REST API?

REST APIs use HTTP methods to perform operations on data. The most common are:

GET: Retrieve data (like reading a book)

POST: Create new data (like adding a new book)

PUT/PATCH: Update existing data (like editing a book)

DELETE: Remove data (like deleting a book)

Fetching Data with fetch()

// Simple GET request

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

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

.then(data => console.log(data))

.catch(error => console.error(error));

// Modern async/await syntax (preferred)

async function fetchUsers() {

try {

const response = await fetch('https://api.example.com/users');

const data = await response.json();

console.log(data);

} catch (error) {

console.error('Error:', error);

}

}

// POST request (creating data)

async function createUser(userData) {

const response = await fetch('https://api.example.com/users', {

method: 'POST',

headers: {

'Content-Type': 'application/json'

},

body: JSON.stringify(userData)

});

return await response.json();

}

Fetching in React with useEffect

import { useState, useEffect } from 'react';

function UserList() {

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

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

const [error, setError] = useState(null);

useEffect(() => {

async function loadUsers() {

try {

const response = await fetch('https://api.example.com/users');

const data = await response.json();

setUsers(data);

} catch (err) {

setError(err.message);

} finally {

setLoading(false);

}

}

loadUsers();

}, []); // Empty array = run once on mount

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

if (error) return <p>Error: {error} </p>;

return (

<ul>

{users.map(user => (

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

))}

</ul>

);

}

💡 Best Practices:

  • • Always handle loading and error states
  • • Use try/catch for error handling
  • • Show loading spinners for better UX
  • • Cache data when possible to reduce API calls
  • • Use environment variables for API URLs

🚀 React Query - Better Data Fetching

React Query (TanStack Query) is a powerful library that makes data fetching much easier! It handles caching, loading states, errors, refetching, and more automatically.

Why React Query?

✅ Automatic caching and background updates

✅ Loading and error states built-in

✅ Automatic retries on failure

✅ Pagination and infinite scroll support

✅ Much less code than manual fetching

// Install: npm install @tanstack/react-query

import { useQuery } from '@tanstack/react-query';

function UserList() {

// That's it! React Query handles everything

const { data, isLoading, error } = useQuery({

queryKey: ['users'],

queryFn: async () => {

const res = await fetch('https://api.example.com/users');

return res.json();

}

});

if (isLoading) return <p>Loading...</p>;

if (error) return <p>Error: {error.message} </p>;

return <ul>{data.map(user => <li key={user.id} >{user.name} </li>)} </ul>;

}

🎯 Practice Project: Weather App

Let's build a weather app that fetches data from an API and manages state!

import { useState } from 'react';

function WeatherApp() {

const [city, setCity] = useState('');

const [weather, setWeather] = useState(null);

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

const fetchWeather = async () => {

setLoading(true);

try {

const API_KEY = 'your_api_key';

const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}`;

const response = await fetch(url);

const data = await response.json();

setWeather(data);

} catch (error) {

console.error(error);

} finally {

setLoading(false);

}

};

return (

<div className="max-w-md mx-auto p-6">

<h1 className="text-3xl font-bold mb-4">Weather App</h1>

<div className="flex gap-2 mb-4">

<input

type="text"

value={city}

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

placeholder="Enter city"

className="flex-1 px-4 py-2 border rounded"

/>

<button

onClick={fetchWeather}

className="px-6 py-2 bg-blue-500 text-white rounded"

>

Search

</button>

</div>

{loading && <p>Loading...</p>}

{weather && (

<div className="bg-white p-6 rounded-lg shadow">

<h2 className="text-2xl font-bold">{weather.name} </h2>

<p className="text-4xl">{Math.round(weather.main.temp - 273.15)} °C</p>

<p className="text-gray-600">{weather.weather[0].description} </p>

</div>

)}

</div>

);

}

🎨 Try Adding:

  • • 5-day forecast
  • • Weather icons
  • • Save favorite cities
  • • Geolocation to get current location
  • • Temperature unit toggle (C/F)

📚 Learning Resources

State Management

API & Data Fetching

🎯 Congratulations!

You've completed the Web Development fundamentals! You now know HTML, CSS, JavaScript, Tailwind CSS, state management, and API integration. You're ready to build real-world web applications. Keep practicing and building projects to solidify your skills!