All files / app/features/userQueries useUsers.ts

100% Statements 39/39
100% Branches 16/16
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 8614x 14x 14x 14x 14x 14x   26x       356x 356x 356x 109x 5x   18x             356x 107x       356x 356x 2x 2x       356x   406x 403x 83x           356x   403x       403x 395x 386x       362x 356x   356x   233x   356x                   330x 330x 330x                
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,
  };
}