DEV Community

Cover image for Do We Really Need Redux for Profile Data? Managing User State with TanStack Query
Farhad Faraji
Farhad Faraji

Posted on

Do We Really Need Redux for Profile Data? Managing User State with TanStack Query

For years, the go-to solution for managing global state in React apps was Redux (and later, lighter options like Zustand). But in many cases, especially when the state comes directly from the server, you don’t need a state management library at all.

This post will walk through a common scenario: keeping a user profile available across the frontend. We’ll see how TanStack Query makes this effortless (with caching, invalidation, and persistence) without Redux or Zustand.


🧩 The Problem

Imagine you have an endpoint /api/me that returns the authenticated user’s profile. You want this profile data:

  • Available across your app (navbar, settings page, etc.)
  • Persisted in memory, without refetching on every render
  • Revalidated only when necessary (e.g., after updating profile)
  • Cleared out on logout

Traditionally, we’d reach for Redux or Zustand to:

  • Fetch profile once
  • Store it in global state
  • Manually update/remove when needed

But TanStack Query does this out of the box.

⚡ Setting Up TanStack Query

First, install the dependencies:

npm install @tanstack/react-query axios
Enter fullscreen mode Exit fullscreen mode

Wrap your app with the QueryClientProvider:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <YourRoutes />
    </QueryClientProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

📦 Defining the Profile Query

Instead of scattering query config across your codebase, it’s best to define a query object and re-use it everywhere:

// profileQuery.ts
import { queryOptions } from '@tanstack/react-query';
import axios from 'axios';

async function fetchProfile() {
  const { data } = await axios.get('/api/me');
  return data;
}

export const profileQuery = queryOptions({
  queryKey: ['profile'],
  queryFn: fetchProfile,
  staleTime: Infinity, // profile doesn’t change often
  gcTime: Infinity,    // keep it cached until explicitly cleared
});
Enter fullscreen mode Exit fullscreen mode

🎯 Using the Profile Across Components

Now you can import and use this query anywhere:

import { useQuery } from '@tanstack/react-query';
import { profileQuery } from './profileQuery';

function Navbar() {
  const { data: profile } = useQuery(profileQuery);
  return <span>Welcome, {profile?.name}</span>;
}

function SettingsPage() {
  const { data: profile } = useQuery(profileQuery);
  return <div>Email: {profile?.email}</div>;
}
Enter fullscreen mode Exit fullscreen mode

✅ Both components read from the same cached query.

✅ No prop drilling.

✅ No Redux/Zustand boilerplate.


🔄 Revalidating Profile After Update

When you update the profile, just invalidate the query so it refetches:

import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

function useUpdateProfile() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (updates) => axios.put('/api/me', updates),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['profile'] });
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

This ensures your UI always has the latest profile, without manual state juggling.


🚪 Handling Logout

On logout, just remove the cached profile query:

function useLogout() {
  const queryClient = useQueryClient();

  return () => {
    // Call your logout API
    queryClient.removeQueries({ queryKey: ['profile'] });
  };
}
Enter fullscreen mode Exit fullscreen mode

This clears the user data instantly from memory.


📌 Takeaways

  • TanStack Query can act as your global store for server state.
  • Use infinite cache time for stable data like profiles, and manually invalidate on updates.
  • Store query configuration (key, fn, cache time) in a query object to re-use cleanly across your app.
  • For logout, simply remove the query.

👉 The result? No reducers, no actions, no global store overhead, just queries.


💡 Pro tip: Use Zustand (or React context) only for client, only UI state (e.g., theme, modal toggles). For server data, TanStack Query has you covered.

Top comments (0)