Today I Learned

React Query with TypeScript

Types for Post resource

// types

export type Post = {
  id: number;
  title: string;
  description: string;
};

export type PostFormData = Omit<Post, 'id'>;

export type PostQueryKey = ['post', { postId: number }];

export type FetchPost = {
  queryKey: PostQueryKey;
};
// api/posts/requests.ts

export const fetchPosts = (): Promise<Post[]> => client.get('/posts');

export const fetchPost = ({ queryKey: [, param] }: FetchPost): Promise<Post> =>
  client.get(`/posts/${param.postId}`);

export const createPost = (data: PostFormData) => client.post('posts', data);

export const editPost = (data: Post) => client.put(`/posts/${data.id}`, data);

export const deletePost = (postId: number) => client.delete(`/posts/${postId}`);
// api/posts/selectors.ts

export const getPosts = (data: any): Post[] => data.data;
export const getPost = (data: any): Post => data.data;
// api/shared.ts

type SelectorsMap = {
  [key: string]: (...arg: any[]) => any;
};

export function handleSelectors<T extends SelectorsMap, K extends keyof T>(
  selectors: T,
  ...additionalParams: any[]
) {
  return function selectorResult(rawData: any) {
    const keys = Object.keys(selectors) as K[];
    const initialData = {} as {
      [Key in K]: ReturnType<T[Key]>;
    };

    return keys.reduce((acc, selectorName) => {
      const selector = selectors[selectorName];

      acc[selectorName] = selector(rawData, additionalParams);
      return acc;
    }, initialData);
  };
}
// api/posts/hooks.ts

export const useGetPosts = ({
  selectors = { posts: getPosts },
  ...options 
} = {}) =>
  useQuery('posts', fetchPosts, {
    select: handleSelectors(selectors),
    ...options,
  });

export const useGetPost = ({
  postId = 0,
  selectors = { post: getPost },
  ...options
} = {}) => {
  const queryKey: PostQueryKey = ['post', { postId }];

  return useQuery(queryKey, fetchPost, {
    select: handleSelectors(selectors),
    ...options,
  });
};

export const useCreatePost = (options = {}) =>
  useMutation(createPost, {
    mutationKey: 'createPost',
    ...options,
  });

export const useEditPost = (options = {}) =>
  useMutation(editPost, {
    mutationKey: 'editPost',
    ...options,
  });

export const useDeletePost = (options = {}) =>
  useMutation(deletePost, {
    mutationKey: 'deletePost',
    ...options,
  });