使用 react-router-dom 成功验证后将用户重定向到他们请求的页面

IT技术 reactjs typescript redirect react-router react-router-dom
2021-05-23 10:48:42

我已经构建了一个公共路由组件,用于登录以在用户未通过身份验证时显示。每当未登录的用户单击受保护的路由时,他将被重定向到登录页面,他可以在其中输入凭据。我想要一种编程方式,以便如果他使用正确的凭据登录,他应该被重定向到他首先尝试访问的页面。例如,如果用户请求个人资料页面,他应该在登录后重定向到该页面,如果用户请求设置页面,也会发生同样的情况。

截至目前,我只能将它们重定向到 home path /有什么方法可以使用重定向,以便它知道用户请求的路径?

这是我当前的公共路由组件代码

export const PublicRoute = ({
    isAuthenticated,
    component: Component,
    ...rest
}: PublicRouteProps) => (
    <Route
        {...rest}
        component={(props: any) => {
            console.log(props.path);
            return isAuthenticated.auth ? (
                <Redirect to='/' />
            ) : (
                <div>
                    <Component {...props} />
                </div>
            );
        }}
    />
);
const mapStateToProps = (state: ReduxStoreState) => ({
    isAuthenticated: state.isAuthenticated
});

export default connect(mapStateToProps)(PublicRoute);

3个回答

你的问题不是那么容易回答的。基本上,您需要记住用户想要访问的路径,以便在用户成功通过身份验证后重定向到该路径。

我在这里为您创建了一个示例您可以在下面找到该示例中的解释和一些代码。

因此,如果用户未通过身份验证,我们将设置应用程序状态的路径。我会修改你的ProtectedRoute

import { useEffect } from 'react';
import { Redirect, Route, RouteProps, useLocation } from 'react-router';

export type ProtectedRouteProps = {
  isAuthenticated: boolean;
  authenticationPath: string;
  redirectPath: string;
  setRedirectPath: (path: string) => void;
} & RouteProps;

export default function ProtectedRoute({isAuthenticated, authenticationPath, redirectPath, setRedirectPath, ...routeProps}: ProtectedRouteProps) {
  const currentLocation = useLocation();

  useEffect(() => {
    if (!isAuthenticated) {
      setRedirectPath(currentLocation.pathname);
    }
  }, [isAuthenticated, setRedirectPath, currentLocation]);

  if(isAuthenticated && redirectPath === currentLocation.pathname) {
    return <Route {...routeProps} />;
  } else {
    return <Redirect to={{ pathname: isAuthenticated ? redirectPath : authenticationPath }} />;
  }
};

为了记住身份验证和重定向路径,我将根据以下模型创建一个上下文:

export type Session = {
  isAuthenticated?: boolean;
  redirectPath: string;
}

export const initialSession: Session = {
  redirectPath: ''
};

据此,上下文如下所示:

import { createContext, useContext, useState } from "react";
import { initialSession, Session } from "../models/session";

export const SessionContext = createContext<[Session, (session: Session) => void]>([initialSession, () => {}]);
export const useSessionContext = () => useContext(SessionContext);

export const SessionContextProvider: React.FC = (props) => {
  const [sessionState, setSessionState] = useState(initialSession);
  const defaultSessionContext: [Session, typeof setSessionState]  = [sessionState, setSessionState];

  return (
    <SessionContext.Provider value={defaultSessionContext}>
      {props.children}
    </SessionContext.Provider>
  );
}

现在你需要让你的应用程序可以使用这个上下文:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import { SessionContextProvider } from './contexts/SessionContext';
import { BrowserRouter } from 'react-router-dom';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <SessionContextProvider>
        <App />
      </SessionContextProvider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

在您的主容器中,您可以应用受保护的路由:

import ProtectedRoute, { ProtectedRouteProps } from "../components/ProtectedRoute";
import { useSessionContext } from "../contexts/SessionContext";
import { Route, Switch } from 'react-router';
import Homepage from "./Homepage";
import Dashboard from "./Dashboard";
import Protected from "./Protected";
import Login from "./Login";

export default function App() {
  const [sessionContext, updateSessionContext] = useSessionContext();

  const setRedirectPath = (path: string) => {
    updateSessionContext({...sessionContext, redirectPath: path});
  }

  const defaultProtectedRouteProps: ProtectedRouteProps = {
    isAuthenticated: !!sessionContext.isAuthenticated,
    authenticationPath: '/login',
    redirectPath: sessionContext.redirectPath,
    setRedirectPath: setRedirectPath
  };

  return (
    <div>
      <Switch>
        <Route exact={true} path='/' component={Homepage} />
        <ProtectedRoute {...defaultProtectedRouteProps} path='/dashboard' component={Dashboard} />
        <ProtectedRoute {...defaultProtectedRouteProps} path='/protected' component={Protected} />
        <Route path='/login' component={Login} />
      </Switch>
    </div>
  );
};

2021 年 3 月更新

我已经更新了上面的答案。React 在从外部组件设置状态时抛出错误。/路径不受保护时,先前的解决方案也不起作用这个问题应该被修复。

此外,我为 React Router 6创建了一个示例

伙计,我认为最好的方法是像这样使用history.push函数componentDidMount

componentDidMount() {
  if(isAuthenticated.auth) {
    this.props.history.push('/profile')
  }
  else {
    this.props.history.push('/login') 
  }
}

在你的 App.js 中

import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      urlToRedirect: null
    }
  }
  componentWillReceiveProps(nextProps) {
    if ( nextProps.location !== this.props.location && 
         !this.props.isAuthenticated && 
         this.props.history.action === 'REPLACE') {
      this.setState({urlToRedirect: this.props.location.pathname});
    }
  }
}
const mapStateToProps = (state: ReduxStoreState) => ({
    isAuthenticated: state.isAuthenticated
});
export default connect(mapStateToProps, null)(withRouter(App));

您可以使用您的 Redux 状态,然后访问该 URL,而不是使用 setState。