Кейс: При заходе в приложение нужно получить данные пользователя, понять - подтвержден ли аккаунт, если подтвержден открыть роуты из
routes. Если не подтвержден - вывести компонент
ErrorLinkedEmail, из него можно отправить повторно ссылку на подтверждение. При переходе по ссылке из email попасть в компонент
EmailConfirmation.
Проблема 1: после окончания запроса в компоненте
EmailConfirmation (запрос идет автоматически при переходе по ссылке) делается редирект на главную - тут-то и начинается боль. А именно - после того, как отрабатывает
apiUser.current(),
isFetching переводится в значение
false, после чего начинается отрисовка
children - но в этот момент store еще не обновился, следовательно, идет вызов компонента
ErrorLinkedEmail (происходит своего рода моргание, компонент отрисовывается на доли секунды).
Проблема 2: после перехода на главную идет дополнительный вызов подтверждения почты из
EmailConfirmation (в сам компонент он вроде как не заходит, возможно, react-query посчитал данные устаревшими и делает перевызов, но это не точно).
P. S. на некорректный нейминг и множественный вызов useSelector из одного namespace просьба не обращать внимание.
App.jsxexport const App = () => {
const dispatch = useDispatch();
const role = useSelector((state) => get(state, 'user.role'));
const user = useSelector((state) => get(state, 'user'));
const emailVerifiedAt = useSelector((state) => get(state, 'user.emailVerifiedAt'));
const messages = useSelector((state) => get(state, 'message.items'));
const routes = useMemo(() => getRoutes(role), [role]);
const routesPaths = useMemo(() => getDiffRoutes(role), [role]);
const redirectURL = useMemo(() => getDefaultRedirect(role), [role]);
return (
<ConfigProvider>
<AdaptivityProvider>
<AppRoot>
<UserProvider>
<BrowserRouter>
<Router history={history}>
<Switch>
{emailVerifiedAt === null && (
<Route path={APP_ROUTES.EMAIL_CONFIRMATION} component={EmailConfirmation} />
)}
{emailVerifiedAt === null && (
<Route component={() => <ErrorLinkedEmail user={user} onReload={() => {}} />} />
)}
{mapperRoutes(routes)}
<Route exact path={routesPaths} component={() => <Redirect to={redirectURL} />} />
<Route component={NotFoundPage} />
</Switch>
</Router>
</BrowserRouter>
</UserProvider>
</AppRoot>
</AdaptivityProvider>
</ConfigProvider>
);
};
const mapperRoutes = (routes) =>
routes.map(({ component: Component, path, exact, isMenu, maxContentWidth }) => (
<Route key={path} path={path} exact={exact}>
<Layout isMenu={isMenu} maxContentWidth={maxContentWidth}>
<Component />
</Layout>
</Route>
));
UserProviderexport const UserProvider = ({ children }) => {
const dispatch = useDispatch();
const { data, isFetching } = useQuery([QUERY_KEYS.USER], () => apiUser.current());
useEffect(() => {
if (data?.id) {
dispatch(userSlice.actions.setUser(data));
}
}, [data, dispatch]);
if (isFetching) {
return <ScreenSpinner />;
}
return children;
};
EmailConfirmationexport const EmailConfirmation = () => {
const { search } = useLocation();
const { userId, hash } = useParams();
const queryClient = useQueryClient();
const { isLoading, isError, refetch } = useQuery(['email-confirmation', userId, hash, search], () =>
apiAuth.emailConfirmation({ userId, hash, search })
);
useEffect(() => () => queryClient.refetchQueries(QUERY_KEYS.USER), [queryClient]);
return (
<View id="confirmation-email" activePanel="main">
<Panel id="main">
<PanelHeader>Подтверждение почты</PanelHeader>
<Group>
{isLoading && <PanelSpinner />}
{isError && <ErrorMessage description="Не удалось подтвердить почту" onAction={refetch} />}
</Group>
</Panel>
</View>
);
};
P. S. Проблему №2 решил:
1. Нужно было использовать useMutation вместо useQuery.
2. Зависимости в useQuery инициировали refetch при unmount.