Skip to main content

React State Management and Routing

When local state is not enough​

If many components need the same data, lift state up, or use context and reducers.

Context​

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

const ThemeContext = createContext("light");

export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

export const useTheme = () => useContext(ThemeContext);

useReducer for complex updates​

function cartReducer(state, action) {
switch (action.type) {
case "add":
return [...state, action.item];
case "remove":
return state.filter((i) => i.id !== action.id);
case "clear":
return [];
default:
return state;
}
}

// const [cart, dispatch] = useReducer(cartReducer, []);
// dispatch({ type: "add", item });

Data fetching states​

Always model loading, error and success.

import { useEffect, useState } from "react";

export function useFetch(url) {
const [state, setState] = useState({ data: null, error: null, loading: true });

useEffect(() => {
const controller = new AbortController();
setState({ data: null, error: null, loading: true });
fetch(url, { signal: controller.signal })
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`))))
.then((data) => setState({ data, error: null, loading: false }))
.catch((error) => {
if (error.name !== "AbortError") setState({ data: null, error, loading: false });
});
return () => controller.abort();
}, [url]);

return state;
}

For production apps consider a data library (TanStack Query, SWR) that adds caching, retries and de-duplication.

Routing with React Router​

import { BrowserRouter, Routes, Route, Link, useParams } from "react-router-dom";

function Post() {
const { id } = useParams();
return <h1>Post {id}</h1>;
}

export default function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link> | <Link to="/posts/1">Post 1</Link>
</nav>
<Routes>
<Route path="/" element={<h1>Home</h1>} />
<Route path="/posts/:id" element={<Post />} />
<Route path="*" element={<h1>Not found</h1>} />
</Routes>
</BrowserRouter>
);
}

Choosing a state tool​

NeedUse
Component-only datauseState
Shared across a subtree, rarely changesContext
Complex update logicuseReducer
Server data (cache, refetch)TanStack Query / SWR
Large app-wide client stateZustand, Redux Toolkit