@lexstile

Как правильно и удобно выстроить роутинг на react?

Кейс: При заходе в приложение нужно получить данные пользователя, понять - подтвержден ли аккаунт, если подтвержден открыть роуты из routes. Если не подтвержден - вывести компонент ErrorLinkedEmail, из него можно отправить повторно ссылку на подтверждение. При переходе по ссылке из email попасть в компонент EmailConfirmation.

Проблема 1: после окончания запроса в компоненте EmailConfirmation (запрос идет автоматически при переходе по ссылке) делается редирект на главную - тут-то и начинается боль. А именно - после того, как отрабатывает apiUser.current(), isFetching переводится в значение false, после чего начинается отрисовка children - но в этот момент store еще не обновился, следовательно, идет вызов компонента ErrorLinkedEmail (происходит своего рода моргание, компонент отрисовывается на доли секунды).

Проблема 2: после перехода на главную идет дополнительный вызов подтверждения почты из EmailConfirmation (в сам компонент он вроде как не заходит, возможно, react-query посчитал данные устаревшими и делает перевызов, но это не точно).

P. S. на некорректный нейминг и множественный вызов useSelector из одного namespace просьба не обращать внимание.

App.jsx
export 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>
  ));

UserProvider
export 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;
};

EmailConfirmation
export 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.
  • Вопрос задан
  • 46 просмотров
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы