Most tutorials teach state management as if every app is a to-do list. Health apps aren't. A hydration tracker, a meal log, or a symptom journal all share a property that to-do apps don't: the data has to survive being wrong for a while. A user logs a meal, corrects it, logs it again, and your UI needs to hold all of that without flickering or losing intent.
This is where useState scattered across components quietly betrays you. Not because it's technically incapable, but because health data has a shape — timestamps, units, provisional vs. confirmed entries — that local component state was never designed to model.
The pattern that actually holds up
Instead of one flat state object, model health data as an event log with a derived "current view":
function useHealthLog(initialEntries = []) {
const [events, setEvents] = useState(initialEntries);
const addEntry = (entry) => {
setEvents((prev) => [
...prev,
{ ...entry, id: crypto.randomUUID(), loggedAt: Date.now(), status: "confirmed" },
]);
};
const correctEntry = (id, updates) => {
setEvents((prev) =>
prev.map((e) => (e.id === id ? { ...e, ...updates, correctedAt: Date.now() } : e))
);
};
return { events, addEntry, correctEntry };
}
The tradeoff: this is more code up front than useState([]). What you get back is an audit trail for free — which matters enormously the first time a user asks "wait, why does my calorie total look off from Tuesday."
Where this breaks down
Event logs get expensive if you're rendering thousands of entries without pagination or windowing. For a daily nutrition log, you're fine. For a multi-year fitness history view, you'll want to paginate the derived view, not the underlying log — keep the log honest, make the render cheap.
The honest takeaway
There's no state management library that fixes a bad data model. Redux, Zustand, plain hooks — pick whichever fits your team's taste. But decide first whether your health data is a log of what happened or a snapshot of what's true right now. Most bugs I've debugged in this space came from conflating the two.
Top comments (0)