Как в React с использованием rtq-query можно отобразить спиннер во время повторного запроса (refetch) при изменении параметров, но при этом не показывать его во время периодического опроса (polling)?

Как лучше реализовать отображение спиннера на странице с элементами items, если при переключении между страницами и изменении фильтров необходимо показывать его, а при фоновом обновлении данных через pollingInterval - нет? Я использую RTK Query, который возвращает `isLoading` и `isFetching`, однако `isLoading` устанавливается в `true` только при первой загрузке компонента, тогда как `isFetching` - при каждом запросе, независимо от того, изменились ли параметры запроса или это обновление через polling. В данный момент у меня есть идея использовать отдельный state и `useEffect`, чтобы различать причины обновления данных, но этот подход кажется излишне сложным. Как обычно решают подобные задачи в таких случаях?
  • 11 октября 2024 г. 11:05
Ответы на вопрос 2
Для задачи, которую вы описываете, действительно можно использовать подход с состоянием и хуком `useEffect`, но также можно сделать это более элегантным образом, используя свойства `isFetching` и различные состояния компонентов.

### Основные идеи

1. **Используйте флаги для управления состоянием**:
   - Создайте локальное состояние `isManuallyFetching` для отслеживания, происходит ли запрос при изменении параметров.
   - Используйте `onSuccess` и `onError` в `refetch` для обновления этого состояния.

2. **Отображение спиннера**:
   - Показывайте спиннер, когда `isFetching` истинно и `isManuallyFetching` также истинно. 
   - Это позволит вам избежать отображения спиннера при `polling`, так как он будет только для ручных обновлений.

### Пример реализации

```jsx
import React, { useEffect, useState } from 'react';
import { useLazyQuery } from '@reduxjs/toolkit/query/react';
import { myApi } from './api'; // Импортируем ваш api

const MyComponent = () => {
  const [isManuallyFetching, setManuallyFetching] = useState(false);

  const {
    data: items,
    isFetching,
    refetch,
    // добавьте другие нужные вам свойства
  } = myApi.useGetItemsQuery();

  // Используйте этот эффект для определения того, нужно ли показывать спиннер
  useEffect(() => {
    if (isFetching && isManuallyFetching) {
      // Включаем спиннер
    }
  }, [isFetching, isManuallyFetching]);

  const handleFilterChange = (newFilters) => {
    setManuallyFetching(true);
    refetch().then(() => setManuallyFetching(false));
  };

  return (
    <div>
      {isFetching && isManuallyFetching && <Spinner />}
      {/* Тут отображение элементов */}
      {items && items.map(item => <Item key={item.id} data={item} />)}
    </div>
  );
};
```

### Объяснение шагов:

1. **Локальное состояние `isManuallyFetching`**:
   - Это состояние будет истинно только тогда, когда происходит ручной запрос из-за изменения фильтров или переключения страниц.

2. **Обновление состояния через `refetch`**:
   - Когда вы вызываете `refetch`, устанавливаете `isManuallyFetching` в `true` перед запросом и сбрасываете его обратно в `false`, когда запрос завершен.

3. **Условное отображение спиннера**:
   - Спиннер отображается только тогда, когда происходят ручные запросы и данные ещё загружаются (при `isFetching` и `isManuallyFetching`).

### Заключение

Такой подход позволит вам контролировать отображение спиннера более точно, избегая излишней сложности. Используйте состояния и эффекты разумно и в большинстве случаев сможете обеспечить адекватное поведение вашего интерфейса.
Вообщем пока я придумал только такое решение: 

export const MyPage: React.FC = () => {
    const itemsPerPage = 50;
    const [searchText, setSearchText] = useState(''),
    [activePage, setActivePage] = useState(1),
    [filters, setFilters] = useState<Filters>({
      status: 'all',
      category: '1'
    });

    const {
        data,
        isLoading,
        isFetching,
        isSuccess,
        refetch,
      } = useSearchItemsQuery(
            {
                sortBy: sortBy,
                startAt: (activePage - 1) * itemsPerPage,
                maxResults: itemsPerPage,
                searchText,
                filters,
              },
        { refetchOnFocus: true, pollingInterval: 30000 },
      );

  // Page loader
  const [showPageLoader, setShowPageLoader] = useState(true);
  useEffect(() => {
    setShowPageLoader(true);
  }, [activePage, itemsPerPage, sortBy, filters]);

  useEffect(() => {
    if (!isFetching) {
      setShowPageLoader(false);
    }
  }, [isFetching]);

    return (
        <div>
            {data && items.map(item => <Item key={item.id} item={item} />)}
            <Spinner show={isLoading || (isFetching && showPageLoader) />
        </div>
    )
}
Похожие вопросы