| name | manage-component-state |
| description | Manage component state and data flow in React components following best practices |
When managing state and data in this Next.js 15 dashboard:
-
Choose the right state management approach:
- Local state (useState): For component-specific data
- Context API: For shared data across multiple components
- Props: For parent-to-child data flow
- Server state: For data fetching (use Server Components when possible)
-
Local state with useState:
"use client";
import { useState } from "react";
export default function Component() {
const [count, setCount] = useState(0);
const [user, setUser] = useState({ name: "", email: "" });
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}
-
Derived state (avoid duplicate state):
const totalRevenue = stats.reduce((sum, stat) => sum + stat.value, 0);
-
Complex state with useReducer:
import { useReducer } from "react";
const initialState = { count: 0, loading: false };
function reducer(state, action) {
switch (action.type) {
case "increment":
return { ...state, count: state.count + 1 };
case "setLoading":
return { ...state, loading: action.payload };
default:
return state;
}
}
export default function Component() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<button onClick={() => dispatch({ type: "increment" })}>
{state.count}
</button>
);
}
-
Shared state with Context:
"use client";
import { createContext, useContext, useState } from "react";
const DashboardContext = createContext(null);
export function DashboardProvider({ children }) {
const [filters, setFilters] = useState({ date: "today" });
return (
<DashboardContext.Provider value={{ filters, setFilters }}>
{children}
</DashboardContext.Provider>
);
}
export function useDashboard() {
const context = useContext(DashboardContext);
if (!context) throw new Error("useDashboard must be used within DashboardProvider");
return context;
}
-
Side effects with useEffect:
import { useEffect, useState } from "react";
export default function Component() {
const [data, setData] = useState([]);
useEffect(() => {
async function fetchData() {
const response = await fetch("/api/data");
const result = await response.json();
setData(result);
}
fetchData();
}, []);
return <div>{/* Render data */}</div>;
}
-
Optimizing state updates:
setCount(prevCount => prevCount + 1);
setCount(count + 1);
setLoading(false);
-
Form state management:
import { useState } from "react";
export default function Form() {
const [formData, setFormData] = useState({
name: "",
email: "",
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const handleSubmit = (e) => {
e.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
name="email"
value={formData.email}
onChange={handleChange}
/>
<button type="submit">Submit</button>
</form>
);
}
-
Avoid common pitfalls:
- Don't mutate state directly: Use spread operator or immutable updates
- Don't call hooks conditionally
- Don't forget cleanup in useEffect
- Avoid storing derived data in state
Examples
Manage dashboard filter state
"use client";
import { useState } from "react";
export default function Dashboard() {
const [dateRange, setDateRange] = useState("last7days");
const [chartType, setChartType] = useState("line");
const filteredData = data.filter(item =>
filterByDateRange(item, dateRange)
);
return (
<div>
<select
value={dateRange}
onChange={(e) => setDateRange(e.target.value)}
>
<option value="last7days">Last 7 Days</option>
<option value="last30days">Last 30 Days</option>
</select>
<DashboardCharts data={filteredData} type={chartType} />
</div>
);
}
Create a custom hook for data fetching
import { useState, useEffect } from "react";
export function useStats() {
const [stats, setStats] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchStats() {
try {
const response = await fetch("/api/stats");
const data = await response.json();
setStats(data);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
fetchStats();
}, []);
return { stats, loading, error };
}
function Dashboard() {
const { stats, loading, error } = useStats();
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{/* Render stats */}</div>;
}
Manage theme state
"use client";
import { useTheme } from "next-themes";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
Toggle Theme
</button>
);
}
Related Files
src/components/theme-provider.tsx
src/components/DashboardCharts.tsx
src/components/LayoutWrapper.tsx
Related Skills
optimize-react-component
debug-component
create-dashboard-page