Задать вопрос
@Andrey1302
FullStack Developer (React.js | Node.js | Nest.js)

Как правильно обрабатывать 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 и архитектуры кода?
  • Вопрос задан
  • 30 просмотров
Подписаться 1 Средний Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
ITK academy Нижний Новгород
от 80 000 до 120 000 ₽
ITK academy Воронеж
от 50 000 до 90 000 ₽