reboottime / React-Develpment

Some notes, thought, articles or source code readings aggregated here about React Web development.
0 stars 0 forks source link

[Architecture] @tanstack/react-query best practices #96

Open reboottime opened 1 month ago

reboottime commented 1 month ago

Overview

What this article is about

This article summarizes the best practices for designing a React app using @tanstack/react-query, under the premise that your restful APIs are well designed. If your API is not resource-oriented and driven by client, consider using GraphQL instead.

The react query version discussed in this article is 5.37.1.



Why @tankstac/react-query?

Upon watching this youtube video, presented by @tanstack/react-query author Tanner Linsley, you will get a brief idea about what problems pushed the born of @tanstack/react-query.

And get what bens @tanstack/react-query brings to u via reading the motivation article



React Query Best Practices

reboottime commented 1 month ago

First Practice: Group services and queries by resources

interface GetAllOrdersParams {
  status?: "pending" | "processing" | "completed" | "cancelled";
  sortBy?: "createdAt" | "totalAmount";
  sortOrder?: "asc" | "desc";
  page?: number;
  limit?: number;
}

class Orders {
  private httpClient: HttpClient;

  constructor(baseUrl: string) {
    this.httpClient = new HttpClient(baseUrl);
  }

  // Query all orders
  async getAllOrders(params?: GetAllOrdersParams): Promise<Order[]> {
    //   your code
  }

  // Query order by ID
  async getOrderById(orderId: string): Promise<Order> {
    //   your code
  }

  // Create a new order
  async createOrderById(orderData: OrderData): Promise<Order> {
    // Your code
  }

  // Update an existing order
  async updateOrderById(
    orderId: string,
    updateData: Omit<Partial<Order>, "id">
  ): Promise<Order> {
    // Your code
  }

  // Delete an order
  async deleteOrderById(orderId: string): Promise<void> {
    //   your code
  }
}
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Orders, GetAllOrdersParams, OrderData, Order } from "./order.services";

import queryClient from 'setup/query-client';

export const ordersHttpClient = new Orders("your-base-url");

export const ordersQueryKeys = {
    orders: ['orders'],
    allOrders: (params) => [...ordersQueryKeys.orders, parmas],
    orderById: (orderId:string) => [ordersQueryKeys.orders, orderId],
}

// export data prefetch method, to preload data. Corresponding http request will not be made if data already cahced
export const prefetchAllOrders = (params: Record<string, any>) {
    return queryClient.fetchQuery({
        queryKey: ordersQueryKeys.allOrders(params),
        queryFn: () => ordersHttpClient.getOrders(params)
    })
}

// export data prefetch method, to preload data. Corresponding http request will not be made if data already cahced
export const prefetchOrderById = (orderId: Order['id']) => {
    return queryClient.fetchQuery({
        queryKey: ordersQueryKeys.orderById(orderId),
        queryFn: () => ordersHttpClient.getOrderById(orderId)
    });
}

export const invalidateOrders = () => {
    return queryClient.invalidate
}

const useAllOrders = (params?: GetAllOrdersParams) => {
  return useQuery(["orders", params], () =>
    ordersHttpClient.getAllOrders(params)
  );
};

const useOrderById = (orderId: string) => {
  return useQuery(["order", orderId], () =>
    ordersHttpClient.getOrderById(orderId)
  );
};

const useCreateOrder = () => {
  const queryClient = useQueryClient();

  return useMutation(
    (orderData: OrderData) => ordersHttpClient.createOrderById(orderData),
    {
      onSuccess: () => {
        queryClient.invalidateQueries(["orders"]);
      },
    }
  );
};

const useUpdateOrder = () => {
  const queryClient = useQueryClient();

  return useMutation(
    ({
      orderId,
      updateData,
    }: {
      orderId: string;
      updateData: Omit<Partial<Order>, "id">;
    }) => ordersHttpClient.updateOrderById(orderId, updateData),
    {
      onSuccess: () => {
        queryClient.invalidateQueries(["orders"]);
        queryClient.invalidateQueries(["order"]);
      },
    }
  );
};

const useDeleteOrder = () => {
  const queryClient = useQueryClient();

  return useMutation(
    (orderId: string) => ordersHttpClient.deleteOrderById(orderId),
    {
      onSuccess: () => {
        queryClient.invalidateQueries(["orders"]);
        queryClient.invalidateQueries(["order"]);
      },
    }
  );
};
reboottime commented 1 month ago

Configure Wisely

Configure your query and query client default options based on your needs:

For example, for a real-time visualization monitoring dashboard, you may want:

import { onlineManager } from '@tanstack/react-query'
reboottime commented 1 month ago

For Realtime data update