Nexjs обновления адресной строки блокирует любые запросы
Есть проблемка что при использование nextjs изменение адресной строки одновременно с запросом tanstack-query, он не выполняется, почему?
Приведу 2 примера: На странице предположим есть 2 компонента AddCategoryModal.tsx и ClassesTable.tsx В AddCategoryModal при открытие мы добавляем в адресную строку что модальное окно открыто, а в ClassesTable это таблица которая делает запрос к дб, получает данные и рисует таблицу.
AddCategoryModal.tsx
useEffect(() => {
if (isOpen) {
const params = new URLSearchParams(searchParams);
params.set('addCategory', 'true');
replace(`${pathname}?${params}`, {
scroll: false,
});
}
else {
const params = new URLSearchParams(searchParams);
params.delete('addCategory');
replace(`${pathname}?${params}`, {
scroll: false,
});
}
}, [isOpen])
Я понимаю что такой useEffect не правильно писать, не очень красиво не в этом суть, это для теста, тут при загрузки страницы, так-как стоит strict mode у нас отрабатывает useEffect со значением isOpen=false
тем самым мы выполняем else где просто удаляем того чего и так нету :) Одновременно с этим выполняем компонент.
ClassesTable.tsx
const allClassesData = queryGetAllClasses();
const {
classes, isError, isLoading, ...rest
} = {
classes: allClassesData?.data?.classesData?.classes,
isError: allClassesData?.isError,
isLoading: allClassesData?.isLoading || allClassesData?.isRefetching,
rest: { ...allClassesData }
};
Тут я использую tanstack-query
export const queryGetAllClasses = () => {
console.log('queryGetAllClasses');
return useQuery({
queryKey: ['getClasses'],
queryFn: async () => await apiGetClasses(),
refetchOnWindowFocus: false,
placeholderData: keepPreviousData,
});
}
Все легко и логично функция на apiGetClasses которая выполняется уже на сервере next.
export const apiGetClasses = async (
// TODO: Реализовать пагинацию и лимиты
): Promise<ApiResponse & {
classesData?: {
classes: ClassesDto[],
totalCount: number,
}
}> => {
await waiting();
console.log('apiGetClasses');
try {
...
}
catch (e) {
console.log('renameImage err:', e);
return { isSuccess: false, status: 'error', message: e.sqlMessage ?? (e.message ?? e) };
}
}
Теперь объясняю что происходит, при загрузке страницы, мы меняем адресную строку из-за useEffect в этот момент отрабатывает queryGetAllClasses внутри консоль console.log('queryGetAllClasses');
отрабатывает, значит мы попадаем туда и все окей, но вот уже apiGetClasses не отрабатывает, и не возвращает данные, запрос просто теряется в небытие.
Другой пример:
При открытие модального окна, ставим setIsOpen((isOpen) => !isOpen)
меняем его на true
в этот момент так-же меняем адресную строку вставляя что модальное окно открыто, вместе с этим делаем запрос на сервер 2 запроса один например(как было у меня) на получения класса по id и второй на получения всех учителей из базы данных. Таким образом query отрабатывает оба раза, но api отрабатывает только первый, так-как на время 2, у нас происходит замена адресной строки, тем самым 2 запрос просто теряется в небытие.
код
const UserEditModal = ({
user,
icon,
label,
}: {
user: UserDto,
icon?: React.ReactNode,
label?: string
}) => {
const searchParams = useSearchParams();
const { replace } = useRouter();
const pathname = usePathname();
const isEdit = searchParams.get('edit');
const [isOpen, setIsOpen] = useState<boolean>(isEdit && isEdit.toString() === user.id.toString() ? true : false);
const [isDisabled, setIsDisabled] = useState<boolean>(false);
const buttonRef = useRef(null);
const onOpenModal = () => {
const params = new URLSearchParams(searchParams);
params.set('edit', `${user.id}`);
replace(`${pathname}?${params}`, {
scroll: false,
});
}
const onCloseModal = () => {
const params = new URLSearchParams(searchParams);
params.delete('edit');
replace(`${pathname}?${params}`, {
scroll: false,
});
}
useEffect(() => {
if (buttonRef?.current && isOpen) {
buttonRef.current.focus();
}
}, [isOpen])
return (
<>
<Button
size='verySmall'
theme='transparent'
onClick={() => setIsOpen((prev) => !prev)}
disabled={isOpen}
>
{icon ? icon : <MdOutlineModeEditOutline size={18} />}
{label && <>
{' '}
{label}
</>}
</Button>
<ModalEditUser
isOpen={isOpen}
setIsOpen={setIsOpen}
className={{
base: 'w-full max-w-[420px] min-h-[270px]'
}}
willOpen={onOpenModal}
willClose={onCloseModal}
>
...
</ModalEditUser>
</>
);
};
export default UserEditModal;
При открытые срабатывает onOpenModal()
который меняет адресную строку внутри формы EditUserForm
const EditUserForm = ({
...
}: {
...
}) => {
const test1 = queryGetClassById(1);
const test2 = queryGetAllTeachers();
...
return (
<form>
...
</form>
);
};
export default EditUserForm;
И в данном примере получим что queryGetClassById
отработает так-как срабатывает перед заменой строки, queryGetAllTeachers
уже не успевает, для имитации на запросе стоит задержка 2 секунды, проблема в том-что при смене адресной строки запрос теряется, и не выполняется, и я не понимаю в чем проблема, может есть идеи?
- Заметил что в nextjs файл 'use server' обрабатывает по одному запросу, тем самым если не менять адресную строку, то начинает отрабатывать первый, а второй ждет, после завершения первого, начинает работать второй, а при изменение адресной строки, если она совпадает с запросам, то он отменяется.