Как лучше реализовать отображение спиннера на странице с элементами items, если при переключении между страницами и изменении фильтров необходимо показывать его, а при фоновом обновлении данных через pollingInterval - нет? Я использую RTK Query, который возвращает `isLoading` и `isFetching`, однако `isLoading` устанавливается в `true` только при первой загрузке компонента, тогда как `isFetching` - при каждом запросе, независимо от того, изменились ли параметры запроса или это обновление через polling. В данный момент у меня есть идея использовать отдельный state и `useEffect`, чтобы различать причины обновления данных, но этот подход кажется излишне сложным. Как обычно решают подобные задачи в таких случаях?
Для задачи, которую вы описываете, действительно можно использовать подход с состоянием и хуком `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`).
### Заключение
Такой подход позволит вам контролировать отображение спиннера более точно, избегая излишней сложности. Используйте состояния и эффекты разумно и в большинстве случаев сможете обеспечить адекватное поведение вашего интерфейса.
Вообщем пока я придумал только такое решение: <br/> <br/> <pre><code class="typescript">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>
)
}</code></pre>