React-Redux обновление страницы перенаправляет на вход в систему, даже если состояние существует

#reactjs #redirect #redux #state #refresh

#reactjs #перенаправление #redux #состояние #обновить

Вопрос:

Я создаю серверное приложение администратора, и оно не будет оставаться на правильном маршруте / странице при обновлении браузера.

Когда пользователь входит в систему, он перенаправляет на /dashboard, обновляет состояние с помощью информации о пользователе и токена доступа, устанавливает токен обновления на сервере в файле cookie httponly.

Когда токен доступа истекает и маршрут получает значение 401, он вызывает токен проверки обновления и получает новый токен доступа и обновляет состояние. Все это отлично работает, за исключением обновления.

Я полагаю, что это может быть как-то связано с HashRouter, может быть? Не уверена, но я выдергиваю волосы, потому что уверена, что мне не хватает чего-то маленького.

При посещении страницы я создал приватный маршрут для всего трафика, отличного от /login .

App.js

 import React, {Component} from 'react';
import {HashRouter, Route, Switch} from 'react-router-dom';
import PrivateRoute from "./components/PrivateRoute";
import './scss/style.scss';

const loading = (
    <div className="pt-3 text-center">
        <div className="sk-spinner sk-spinner-pulse"></div>
    </div>
)

// Pages
const Login = React.lazy(() => import('./views/pages/login/Login'));

// Containers
const TheLayout = React.lazy(() => import('./containers/TheLayout'));

class App extends Component {

    render() {
        return (
            <HashRouter>
                <React.Suspense fallback={loading}>
                    <Switch>
                        <Route exact path="/login" name="Login" render={props => <Login {...props}/>}/>
                        <PrivateRoute path="/" name="Home" render={props => <TheLayout {...props}/>}/>
                    </Switch>
                </React.Suspense>
            </HashRouter>
        );
    }
}

export default App;

 

В PrivateRoute я помещаю начальную проверку, есть ли токен и состояние isLoggedIn, если нет, он отправляет мою функцию токена обновления для обновления состояния, которая также функционирует должным образом, но все равно перенаправляет на / login .

PrivateRoute.js

 import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {useDispatch, useSelector} from "react-redux";
import axios from "axios";
import {eraseToken, getRefreshedToken} from "../store/auth";
import store from "../store";

axios.defaults.withCredentials = true;
axios.interceptors.request.use(req => {
    console.log(req)
    const token = store.getState().auth.user.token;
    if (token) {
        req.headers.Authorization = 'Bearer '   token;
    } else {
        //req.headers.Authorization = null;
    }
    return req;
});

axios.interceptors.response.use(res => {
        return res;
    }, async (error) => {
        if (401 === error.response.status) {
            if ('/refresh' !== error.response.config.url) {
                await store.dispatch(getRefreshedToken());
            } else {
                store.dispatch(eraseToken());
            }
        } else {
            return Promise.reject(error);
        }
    }
);

const PrivateRoute = ({render: Component, ...rest}) => {

    const dispatch = useDispatch();
    let token = useSelector(state => state.auth.user.token);
    let isLoggedIn = useSelector(state => state.auth.isLoggedIn);
    console.log(token, isLoggedIn);
    
    if (!isLoggedIn || !token) {
        dispatch(getRefreshedToken());
    }

    // check again after state update
    token = useSelector(state => state.auth.user.token);
    isLoggedIn = useSelector(state => state.auth.isLoggedIn);

    return (
        <Route {...rest} render={props => (
            (token amp;amp; isLoggedIn)
                ? <Component {...props} />
                : <Redirect to={{pathname: '/login', state: {from: props.location}}}/>
        )}/>
    )
}

export default PrivateRoute;
 

This is where I’m confused. Once I log in I get redirected fine to /dashboard because token etc exists in state, however if I refresh the page while at /dashboard it calls dispatch(getRefreshedToken()); which works fine, and updates the state with the correct values, which should then just render the component (I assumed) instead of redirecting back to /login.

If I manually change the url and replace /login with /dashboard it views the dashboard fine still as the state exists properly.

The PrivateRoute renders a component called TheLayout, which renders a component called TheContent which maps routes to views to display.

routes.js

 const routes = [
  { path: '/', exact: true, name: 'Home' },
  { path: '/dashboard', name: 'Dashboard', component: Dashboard },
  { path: '/users', name: 'Users', component: Users }
]

export default routes;
 

TheContent.js

 import React, {Suspense} from 'react';
import {Redirect, Route, Switch} from 'react-router-dom';
import {CContainer, CFade} from '@coreui/react';

// routes config
import routes from '../routes';

const loading = (
    <div className="pt-3 text-center">
        <div className="sk-spinner sk-spinner-pulse"></div>
    </div>
)

const TheContent = () => {
    return (
        <main className="c-main">
            <CContainer fluid>
                <Suspense fallback={loading}>
                    <Switch>
                        {routes.map((route, idx) => {
                            return route.component amp;amp; (
                                <Route
                                    key={idx}
                                    path={route.path}
                                    exact={route.exact}
                                    name={route.name}
                                    render={props => (
                                        <CFade>
                                            <route.component {...props} />
                                        </CFade>
                                    )}/>
                            )
                        })}
                        <Redirect from="/" to="/dashboard"/>
                    </Switch>
                </Suspense>
            </CContainer>
        </main>
    )
}

export default React.memo(TheContent);
 

Не уверен, что это может иметь какое-то отношение к этому?

Обновить

Я понял, что код не ожидал отправки для завершения обновления состояния, поэтому я изменил PrivateRoute, чтобы вместо этого использовать useEffect с зависимостями, и заставил его работать так, как мне нужно.

Однако, пытаясь лучше понять useEffect, он запускает мое обновление и вызывает сервер несколько раз после истечения срока действия моего файла cookie. Есть ли какой-нибудь способ предотвратить это? Вот мой обновленный частный маршрут.

 const PrivateRoute = ({render: Component, ...rest}) => {

    const dispatch = useDispatch();
    const history = useHistory();
    let token = useSelector(state => state.auth.user.token);
    let isLoggedIn = useSelector(state => state.auth.isLoggedIn);
    let isRefreshingToken = useSelector(state => state.auth.isRefreshingToken);

    useEffect(() => {
        if (!isLoggedIn amp;amp; !token amp;amp; !isRefreshingToken) {
            (async () => {
                await dispatch(getRefreshedToken())
                    .catch(err => {
                        history.push('/login');
                    });
            })();
        }
    }, [token, isLoggedIn, isRefreshingToken, dispatch, history])

    return (
        <Route {...rest} render={props => (
            <Component {...props} />
        )}/>
    )
}