Mastering Query Keys in TanStack Query for Optimal Caching


Query keys are fundamental to how TanStack Query caches, organizes, and identifies data. They enable efficient caching, updating, and invalidation of queries, making them an essential tool for managing server state effectively.


In this tutorial, you will learn:

  • What query keys are and how they work.
  • How to structure query keys for optimal caching.
  • Practical examples of query key usage.


Step 1: What Are Query Keys?

A query key is a unique identifier for a query. It ensures that data fetched from a server is stored and retrieved correctly from the cache. Query keys can be simple strings or complex arrays.


Examples:

Simple key: ['users']

Key with parameters: ['user', userId]

Query keys are essential because:


They identify unique data sets in the cache.

They enable efficient cache invalidation and refetching.


Step 2: Setting Up TanStack Query

Before diving into query keys, ensure your project is set up with TanStack Query.


Install TanStack Query

npm install @tanstack/react-query

npm install @tanstack/react-query-devtools


Set Up the Query Client

App.js

import React from 'react';

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

import { ReactQueryDevtools } from '@tanstack/react-query-devtools';


const queryClient = new QueryClient();


function App() {

  return (

    <QueryClientProvider client={queryClient}>

      <h1>Mastering Query Keys</h1>

      <UsersList />

      <ReactQueryDevtools initialIsOpen={false} />

    </QueryClientProvider>

  );

}


export default App;


Step 3: Using Simple Query Keys

A simple query key is a single identifier for fetching data.


Example: Fetching All Users

UsersList.js

import React from 'react';

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


const fetchUsers = async () => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users');

  return response.json();

};


export function UsersList() {

  const { data, isLoading } = useQuery(['users'], fetchUsers);


  if (isLoading) return <p>Loading users...</p>;


  return (

    <ul>

      {data.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}


Explanation:

The query key ['users'] uniquely identifies this data set.

Data is fetched once and cached for reuse.


Step 4: Structuring Query Keys with Parameters

Use query keys with parameters to fetch and cache data for specific entities or subsets.


Example: Fetching a Single User

UserDetails.js

import React from 'react';

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


const fetchUserById = async ({ queryKey }) => {

  const [, userId] = queryKey;

  const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);

  return response.json();

};


export function UserDetails({ userId }) {

  const { data, isLoading } = useQuery(['user', userId], fetchUserById);


  if (isLoading) return <p>Loading user details...</p>;


  return (

    <div>

      <h2>{data.name}</h2>

      <p>Email: {data.email}</p>

      <p>Phone: {data.phone}</p>

    </div>

  );

}


Explanation:

The query key ['user', userId] ensures each user's data is stored separately.

TanStack Query can differentiate between ['user', 1] and ['user', 2].


Step 5: Query Key Best Practices

1. Use Descriptive Keys

Ensure keys are descriptive and meaningful.

['products', categoryId]


2. Maintain Consistent Structures

Keep query keys consistent throughout your application.


3. Leverage Nesting for Complex Data

For hierarchical or related data:

['projects', projectId, 'tasks']



Step 6: Invalidating Query Keys

Invalidate specific queries to refetch fresh data.

Example: Invalidating a Query

AddUserForm.js

import React, { useState } from 'react';

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


const addUser = async (newUser) => {

  const response = await fetch('https://jsonplaceholder.typicode.com/users', {

    method: 'POST',

    headers: { 'Content-Type': 'application/json' },

    body: JSON.stringify(newUser),

  });

  return response.json();

};


export function AddUserForm() {

  const [name, setName] = useState('');

  const queryClient = useQueryClient();


  const mutation = useMutation(addUser, {

    onSuccess: () => {

      queryClient.invalidateQueries(['users']); // Invalidate the 'users' query

    },

  });


  const handleSubmit = (e) => {

    e.preventDefault();

    mutation.mutate({ name });

    setName('');

  };


  return (

    <form onSubmit={handleSubmit}>

      <input

        type="text"

        value={name}

        onChange={(e) => setName(e.target.value)}

        placeholder="Enter name"

      />

      <button type="submit">Add User</button>

    </form>

  );

}


Explanation:

invalidateQueries(['users']) marks the ['users'] query as stale, triggering a refetch.


Step 7: Dependent Queries

Use query keys to handle dependent queries where one query relies on another's result.

Example: Fetching Tasks for a Project

const fetchTasks = async ({ queryKey }) => {

  const [, projectId] = queryKey;

  const response = await fetch(`https://api.example.com/projects/${projectId}/tasks`);

  return response.json();

};


export function ProjectTasks({ projectId }) {

  const { data, isLoading } = useQuery(['project', projectId, 'tasks'], fetchTasks);


  if (isLoading) return <p>Loading tasks...</p>;


  return (

    <ul>

      {data.map((task) => (

        <li key={task.id}>{task.title}</li>

      ))}

    </ul>

  );

}


Explanation:

Query key ['project', projectId, 'tasks'] ensures tasks are cached per project.

By mastering query keys, you can:

  • Organize cached data efficiently.
  • Fetch data dynamically based on parameters.
  • Use query keys for targeted cache invalidation.

With this understanding, you can harness TanStack Query's full potential for optimal caching and performance in your applications. Hope this is helpful, and I apologize if there are any inaccuracies in the information provided.

Comments

Popular posts from this blog

Integrating PHP with Message Queues RabbitMQ Kafka

FastAPI and UVLoop: The Perfect Pair for Asynchronous API Development

Konfigurasi dan Instalasi PostgreSQL Secara Lengkap di Windows Linux dan MacOS