DEV Community

Shashank Trivedi
Shashank Trivedi

Posted on

# πŸ““ TanStack Query: Core Concepts & Summary

1. Introduction: What is TanStack Query?

TanStack Query (formerly React Query) is a framework-agnostic state management library designed specifically to manage Server Stateβ€”handling data fetching, caching, background updating, and cache invalidation.


2. Server State vs. Client State & The Memory Reality

  • Client State: Owned and controlled entirely by the browser (e.g., isModalOpen, selected UI theme).
  • Server State: Owned by the remote backend database (e.g., user profiles, posts, cart items). The browser only holds a read-only temporary snapshot.

Where is data physically stored?

  • Physical Location: By default, cached data lives strictly in the browser tab's JavaScript RAM (In-Memory).
  • Backend ("The Server"): Refers to your remote API/database (regardless of whether it runs on Kubernetes, Docker containers, serverless functions, or bare metal).
  • Optional Persistence: You can opt to sync this RAM cache to localStorage, sessionStorage, or IndexedDB using TanStack Query Persisters.
import React, { useState } from 'react';
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
  useMutation,
  useQueryClient,
} from '@tanstack/react-query';

// 1. Initialize QueryClient (manages the RAM cache)
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 10000, // Data stays fresh in RAM for 10 seconds
    },
  },
});

// Mock API functions
async function fetchPost(postId) {
  const res = await fetch(`[https://jsonplaceholder.typicode.com/posts/$](https://jsonplaceholder.typicode.com/posts/$){postId}`);
  if (!res.ok) throw new Error('Network error');
  return res.json();
}

async function createPost(newPost) {
  const res = await fetch('[https://jsonplaceholder.typicode.com/posts](https://jsonplaceholder.typicode.com/posts)', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newPost),
  });
  return res.json();
}

// 2. Query Component (Fetching & Reading Data)
function PostViewer() {
  const [postId, setPostId] = useState(1);

  const { data, isLoading, isFetching, error } = useQuery({
    queryKey: ['post', postId], // Cache key
    queryFn: () => fetchPost(postId),
  });

  return (
    <div>
      <div>
        <button onClick={() => setPostId(1)}>Load Post 1</button>
        <button onClick={() => setPostId(2)}>Load Post 2</button>
      </div>

      {isFetching && <p style={{ color: 'orange' }}>Updating cache in background...</p>}

      {isLoading ? (
        <p>Loading initial data...</p>
      ) : error ? (
        <p style={{ color: 'red' }}>Error: {error.message}</p>
      ) : (
        <div>
          <h3>Post #{data.id}: {data.title}</h3>
          <p>{data.body}</p>
        </div>
      )}
    </div>
  );
}

// 3. Mutation Component (Updating Data & Invalidating Cache)
function CreatePostForm() {
  const [title, setTitle] = useState('');
  const client = useQueryClient();

  const mutation = useMutation({
    mutationFn: createPost,
    onSuccess: () => {
      // Mark cached post as stale to trigger background refetch
      client.invalidateQueries({ queryKey: ['post', 1] });
      setTitle('');
      alert('Post created! Cache invalidated.');
    },
  });

  const handleSubmit = (e) => {
    e.preventDefault();
    mutation.mutate({ title, body: 'Sample content', userId: 1 });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Enter post title..."
        value={title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <button type="submit" disabled={mutation.isPending}>
        {mutation.isPending ? 'Saving...' : 'Add Post'}
      </button>
    </form>
  );
}

// 4. Main Application Wrapper
export default function App() {
  return (
    <QueryClientProvider client="{queryClient}">
      <PostViewer/>
      <hr />
      <CreatePostForm/>
    </QueryClientProvider>
  );
}```

Enter fullscreen mode Exit fullscreen mode

Top comments (0)