All files / app/features/userQueries useUsers.ts

100% Statements 39/39
100% Branches 14/14
100% Functions 15/15
100% Lines 36/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 8613x 13x 13x 13x 13x 13x   26x       508x 508x 508x 108x 5x   18x             508x 106x       508x 508x 2x 2x       508x   558x 555x 82x           508x   555x       555x 547x 538x       514x 508x   508x   387x   508x                   482x 482x 482x                
import { useQueries, useQueryClient } from "@tanstack/react-query";
import { userKey } from "features/queryKeys";
import { userStaleTime } from "features/userQueries/constants";
import { useCallback, useEffect, useRef } from "react";
import { service } from "service";
import { arrayEq } from "utils/arrayEq";
 
export default function useUsers(
  ids: (number | undefined)[],
  invalidate = false,
) {
  const queryClient = useQueryClient();
  const idsRef = useRef(ids);
  const handleInvalidation = useCallback(() => {
    if (invalidate) {
      queryClient.invalidateQueries({
        predicate: (query) =>
          query.queryKey[0] === userKey()[0] &&
          !!idsRef.current.includes(query.queryKey[1] as number),
        // tells v5 to immediately refetch active observers after invalidation
        refetchType: "active",
      });
    }
  }, [invalidate, queryClient]);
  useEffect(() => {
    handleInvalidation();
  }, [handleInvalidation]);
 
  //arrays use reference equality, so you can't use ids in useEffect directly
  useEffect(() => {
    if (!arrayEq(idsRef.current, ids)) {
      idsRef.current = ids;
      handleInvalidation();
    }
  });
 
  const queries = useQueries({
    queries: ids
      .filter((id): id is number => !!id)
      .map((id) => ({
        queryFn: () => service.user.getUser(id.toString()),
        queryKey: userKey(id),
        staleTime: userStaleTime,
      })),
  });
 
  const errors = queries
    .map((query) =>
      query.error && typeof (query.error as Error).message === "string"
        ? (query.error as Error).message
        : undefined,
    )
    .filter((e): e is string => typeof e === "string");
  const isPending = queries.some((query) => query.isPending);
  const isFetching = queries.some((query) => query.isFetching);
 
  // If at least one user query is not loading (i.e. has data loaded before), whilst
  // some other (likely new) queries are fetching, then it's a refetch
  const isRefetching = !queries.every((query) => query.isLoading) && isFetching;
  const isError = !!errors.length;
 
  const usersById = isPending
    ? undefined
    : new Map(queries.map((q, index) => [ids[index], q.data]));
 
  return {
    data: usersById,
    errors,
    isError,
    isFetching,
    isLoading: isPending,
    isRefetching,
  };
}
 
export function useUser(id: number | undefined, invalidate = false) {
  const result = useUsers([id], invalidate);
  return {
    data: result.data?.get(id),
    error: result.errors.join("\n"),
    isError: result.isError,
    isFetching: result.isFetching,
    isLoading: result.isLoading,
  };
}