All files / app/features/userQueries useUsers.ts

100% Statements 39/39
100% Branches 16/16
100% Functions 15/15
100% Lines 35/35

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 7817x 17x 17x 17x 17x 17x   26x 318x 318x 318x 123x 5x   18x           318x 121x       318x 318x 2x 2x       318x   368x 365x 90x           318x   365x   365x 357x 348x       324x 318x   318x   318x                   292x 292x 292x                
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,
  };
}