Keep frontend state management simple. Use when deciding state management strategy, avoiding unnecessary state libraries, choosing between local and global state, or designing data flow for content-focused applications.
useState) — promote to shared state only when proven necessary// This is sufficient for 90% of content site interactivity
function SearchFilter() {
const [query, setQuery] = useState('');
const filtered = items.filter(item =>
item.title.toLowerCase().includes(query.toLowerCase())
);
return (
<>
<input value={query} onChange={e => setQuery(e.target.value)} />
<ResultList items={filtered} />
</>
);
}
// Use URL params instead of in-memory state for filterable views
function TagPage() {
const { tag } = useParams(); // State lives in the URL
const docs = getDocsByTag(tag); // Derived from URL
return <DocList docs={docs} />;
}
// CORRECT: Derive from source
function Dashboard({ items }) {
const total = items.length; // Derived
const completed = items.filter(i => i.done).length; // Derived
return <Stats total={total} completed={completed} />;
}
// WRONG: Storing derived values separately
function Dashboard({ items }) {
const [total, setTotal] = useState(0);
const [completed, setCompleted] = useState(0);
useEffect(() => {
setTotal(items.length);
setCompleted(items.filter(i => i.done).length);
}, [items]); // Unnecessary sync
}
// Docusaurus provides data at build time — no runtime state needed
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
function SiteTitle() {
const { siteConfig } = useDocusaurusContext();
return <h1>{siteConfig.title}</h1>; // Build-time data, not state
}
useEffect that syncs computed values into separate state variablesuseEffect syncing one state variable to another