Data Management vs State Management in React and Why Mixing Them Hurts

August 2025 · Derick Zr · 3 minutes read

I stored a user profile in Zustand.

Fetched it once on app load. Used it everywhere. Felt smart.

Then the user updated their profile in another tab. My app still showed the old data.

I was mixing data management with state management. Big mistake.

Here's the difference and why it matters.

Data Management (Server State)

Server state is data that:

  • Lives in your backend (not the browser)

  • Can change without your app knowing

  • Needs features like caching, re-fetching, pagination, and background updates

Examples:

  • User profile

  • Product catalog

  • Analytics reports

For this, I use TanStack Query.
It acts like a smart global store for server data:

  • Fetch once, reuse anywhere in the app

  • Auto re-fetch when the data might be stale

  • Cache between navigations

State Management (Client State)

Client state is data that:

  • Exists only in the browser

  • Is often tied to UI interactions

  • Does not need to be synced with a server

Examples:

  • Modal open/close state

  • Sidebar toggle

  • Active tab in a component

  • Form input values

For this, plain React state (useState) or lightweight libraries are enough.

Example: User Profile (Server State)

What I did wrong:

// profileStore.js (Zustand)
import create from 'zustand';
 
export const useProfileStore = create((set) => ({
  profile: null,
  setProfile: (profile) => set({ profile }),
}));
 
// App.jsx
const { setProfile } = useProfileStore();
useEffect(() => {
  fetch('/api/me')
    .then((res) => res.json())
    .then(setProfile);
}, []);
 

Problems:

  • No cache invalidation
  • No automatic re-fetch
  • Changes in other tabs? Invisible

What I should have done:

import { useQuery } from '@tanstack/react-query';
 
function useProfile() {
  return useQuery({
    queryKey: ['profile'],
    queryFn: () => fetch('/api/me').then((res) => res.json())
  });
}
 
// Anywhere in your app:
const { data: profile, isLoading } = useProfile();
 

Benefits:

  • Cache across the app
  • Auto re-fetch when needed
  • Handles loading & error states out of the box

Example 2: Sidebar Toggle — Client State

Here, we don’t need TanStack Query — it’s purely UI state.

function Sidebar() {
  const [isOpen, setIsOpen] = useState(false);
 
  return (
    <>
      <button onClick={() => setIsOpen(!isOpen)}>
        Toggle Sidebar
      </button>
      {isOpen && <div className="sidebar">I’m open!</div>}
    </>
  );
}
 

This state:

  • Doesn’t come from a server
  • Doesn’t need caching or refetching
  • Lives entirely in the browser

Key Takeaways

  • Server State (Data Management)
    Use TanStack Query, SWR, or similar tools to manage data that comes from a backend.

  • Client State (State Management)
    Use React state or a small store for UI interactions and local-only state.

Separating the two keeps your code simpler, more predictable, and easier to maintain — especially as your app grows.

Interactive Example

See the difference in action with this interactive demo:

Server State (The Right Way)

React State

Data from API - should be managed by TanStack Query, SWR, etc.

Client State (UI Only)

React State

UI interactions - perfect for React useState

Current UI State

Sidebar: Closed
Active Tab: profile
Selected User ID: None

Wrong Approach: Server Data in LocalStorage

LocalStorage

This creates stale data problems - avoid this pattern!

⚠️ Why This Is Wrong

  • • Data becomes stale (not synced with server)
  • • No automatic refetching
  • • Manual cache invalidation needed
  • • Duplicates server state management logic

Test the Difference:

  1. 1. Select a user (Alice) in both sections above
  2. 2. Click "Simulate Server Update" to change Alice's data
  3. 3. Notice: Server State updates automatically, LocalStorage doesn't
  4. 4. This is why server data should never be in localStorage!
Data Management vs State Management in React and Why Mixing Them Hurts