Как правильно обрабатывать isFetching для одного и того же запроса в нескольких компонентах (React Query)?

Есть запрос:

export const useGetClients = (params?: GetClientsRequest) =>
  useQuery({
    queryKey: ['clients', 'list', params],
    queryFn: () => ClientClient.getClientApiInstance().getClients(params),
  });

На странице два основных компонента: таблица и кнопка, открывающая сайдбар.

Таблица:

const Wallets = () => {
  const { wallets, isLoading, isFetching } = useGetWallets();

  return (
    <div className="flex flex-col gap-4">
      <div className="flex flex-wrap items-center justify-between gap-2">
        <DepositFundsButton />
      </div>
      <DataTable
        columns={Columns}
        data={wallets}
        isLoading={isLoading}
        isFetching={isFetching}
      />
    </div>
  );
};

где:

export const useGetWallets = () => {
  const {
    data: accounts,
    isLoading: isAccountsLoading,
    isFetching: isAccountsFetching,
  } = useGetLedgerAccounts();

  const {
    data: clients,
    isLoading: isClientsLoading,
    isFetching: isClientsFetching,
  } = useGetClients({
    clientType: ClientType.Client,
  });

  const accountsWithClientName: AccountWithClientName[] =
    accounts && clients
      ? accounts.map((account) => ({
          ...account,
          context: {
            ...account.context,
            ...(account.context.clientId && {
              clientName: clients.clients.find(
                (client) => client.id === account.context.clientId,
              )?.name,
            }),
          },
        }))
      : [];

  return {
    wallets: accountsWithClientName,
    isLoading: isAccountsLoading || isClientsLoading,
    isFetching: isAccountsFetching || isClientsFetching,
  };
};

При нажатии на кнопку Deposit funds открывается сайдбар с формой. В форме тот же запрос вызывается повторно (с теми же параметрами), чтобы получить список клиентов для выпадающего списка:

export const DepositFundsForm = ({ onClose }: DepositFundsFormProps) => {
  const { data, isFetching: isClientsFetching } = useGetClients({
    clientType: ClientType.Client,
  });

  return (
    <>
      <Form {...methods}>
        <form className="space-y-6 overflow-y-auto px-4">
          <SelectField
            name="clientId"
            loading={isClientsFetching}
            control={control}
            label="Client"
            placeholder="Client"
            options={clientOptions}
            className="min-w-[300px]"
          />
        </form>
      </Form>
      <SheetFooter>
        <SheetClose asChild>
          <Button variant="secondary">Cancel</Button>
        </SheetClose>
        <Button onClick={handleSubmit(onSubmit)} isLoading={isSubmitting}>
          Deposit
        </Button>
      </SheetFooter>
    </>
  );
};

Проблема: Когда форма открыта, я вижу два спиннера — один в таблице и второй в сайдбаре. С UX точки зрения это выглядит неправильно, ведь запрос одинаковый и данные уже есть в кеше.

Возможные решения:

  • Показывать спиннер в таблице только если isAccountsFetching, а не при isAccountsFetching || isClientsFetching.
  • Передавать дополнительный ключ запроса (query key) из таблицы или сайдбара, чтобы у них были разные ключи и независимые состояния.
  • Обернуть таблицу и кнопку с сайдбаром в контекст-провайдер, где данные клиентов будут загружаться один раз и шариться между компонентами. В случае с последним решением возникают вопросы: a) что показывать, пока в провайдере идёт загрузка клиентов — скелетон вместо таблицы? b) если форма будет использоваться в других местах, нужно всегда оборачивать её в провайдер, что может быть неудобно

Вопрос: Какой подход здесь будет наиболее правильным с точки зрения UX и архитектуры кода?


Ответы (0 шт):