React Router v5.0 Nested Routes

后端 未结 4 1528
天涯浪人
天涯浪人 2020-12-15 01:30

I\'m building a react app and I can\'t make the routing work.

  1. I need one common layout (header, footer) for a few Auth routes (/login, si

相关标签:
4条回答
  • It took me some time to find the answer that I favoured. Both @johnny-peter & @gaurab-kc solutions were great and they tought me about React's routing mechanism.

    @johnny-peter 's solution had disadvantage of forcing me to put a prefix for all auth related routes under /auth/... (e.g. /auth/login & auth/sign-up) which I didn't wanted.

    @gaurab-kc solution was supporting only one set of routes.. so if user was already signed up, he couldn't visit the /login route anymore.

    Up till recently I used my own solution which had the problem of "defeating the whole purpose of a common header and footer." as @johnny-peter mentioned and it was down-voted few times as it should be.

    Now I'm using another solution:

    <Router history={browserHistory}>
        <Switch>
            <Redirect exact from="/" to="/home"/>
            <Route exact path={["/login", "/sign-up", ...]}>
                <AuthLayout>
                    <Switch>
                        <Route
                            path="/login"
                            component={LoginPage}
                        />
                        <Route
                            path="/sign-up"
                            component={SignUpPage}
                        />
                    </Switch>
                </AuthLayout>
            </Route>
            <Route exact path={[
                "/home",
                "/dashboard",
                ...
            ]}>
                <SiteLayout>
                    <Switch>
                        <Route
                            path="/home"
                            component={HomePage}
                        />
                        <Route
                            path="/dashboard"
                            component={DashboardPage}
                        />
                    </Switch>
                </SiteLayout>
            </Route>
            <Route path="*" component={NotFoundPage}/>
        </Switch>
    </Router>
    

    which prevents all the above disadventages. It's allows me to:

    1. Use a layout for each section which isn't re-rendered on route change.
    2. Doesn't forcing me to add any prefix to routes.
    3. All routes are working at the same time, letting users to route back into the /login or other auth routes without logout first.

    The only disadvantage of this solution is having more code and duplicating the routes, but it's a cost I'm willing to pay.

    0 讨论(0)
  • 2020-12-15 01:57

    Each of your layout should have a path component to differentiate from other layouts.

    For example

    Auth layouts could reside under /auth eg, login would /auth/login, signup would be /auth/signup

    App layout could go under /app eg, dashboard would be /app/dashboard, home would be /app/home

    Working Demo

    App.js

    import { Switch, BrowserRouter, Route, Redirect } from "react-router-dom";
    
    function App() {
      return (
        <BrowserRouter>
          <Layouts />
        </BrowserRouter>
      );
    }
    

    Layouts.js

    const NotFound = () => <h1>Not Found</h1>;
    
    function Layouts() {
      return (
        <Switch>
          <Route path="/auth" component={AuthLayout} />
          <Route path="/app" component={AppLayout} />
          <Route path="/" component={NotFound} />
        </Switch>
      );
    }
    

    AuthLayout

    const Signup = () => <p>Login</p>;
    const Login = () => <p>Sign up</p>;
    
    function AuthLayout() {
      return (
        <div>
          <h1>Auth Layout</h1>
          <Route path="/auth/signup" exact component={Signup} />
          <Route path="/auth/login" exact component={Login} />
          <Redirect from="/auth" to="/auth/login" exact />
        </div>
      );
    }
    

    AppLayout

    const Home = () => <p>Home</p>;
    const Dashboard = () => <p>Dashboard</p>;
    
    function AppLayout() {
      return (
        <div>
          <h1>App Layout</h1>
          <Route path="/app/home" exact component={Home} />
          <Route path="/app/dashboard" exact component={Dashboard} />
          <Redirect from="/app" to="/app/home" exact />
        </div>
      );
    }
    

    Also if you want to protect certain routes from being rendered if not authenticated, then you can create a PrivateRoute component that would redirect to auth layout if not authenticated.

    PrivateRoute.js

    const PrivateRoute = ({ component: Component, ...rest }) => (
      <Route
        {...rest}
        render={props => sessionStorage.token // your auth mechanism goes here
          ? <Component {...props} />
          : <Redirect to={{ pathname: '/auth' }} />}
      />
    );
    

    You can use this PrivateRoute component instead of react-router's Route component.

    Eg:

    <PrivateRoute path="/app" component={AppLayout} />

    0 讨论(0)
  • 2020-12-15 01:58

    EDIT: I have answered this question with another solution.


    Both solutions by @Gaurab Kc & @johnny peter are great though I've ended up doing something like that:

    <Router history={history}>
        <Switch>
            <PrivateRoute
                path="/home"
                component={HomePage}>
            </PrivateRoute>
            <PrivateRoute
                path="/dashboard"
                component={DashboardPage}>
            </PrivateRoute>
            <AuthRoute
                path="/login"
                component={LoginPage}>
            </AuthRoute>
            <AuthRoute
                path="/sign-up"
                component={SignUpPage}>
            </AuthRoute>
            <Route path="*" component={NotFoundPage}/>
        </Switch>
    </Router>
    

    The AuthRoute & PrivateRoute are something like that:

    interface PrivateRouteProps extends RouteProps {
        component: any;
    }
    
    const PrivateRoute = (props: PrivateRouteProps) => {
        const {component: Component, ...rest} = props;
    
        return (
            <Route
                {...rest}
                render={(routeProps) =>
                    localStorage.getItem('user') ? (
                        <div>
                           ... // here is the app header
                           <Component {...routeProps} />
                           .. // here is the app footer
                        </div>
                    ) : (
                        <Redirect
                            to={{
                                pathname: '/login',
                                state: {from: routeProps.location}
                            }}
                        />
                    )
                }
            />
        );
    };
    
    export default PrivateRoute;
    
    interface AuthRouteProps extends RouteProps {
        component: any;
    }
    
    const AuthRoute = (props: AuthRouteProps) => {
        const {component: Component, ...rest} = props;
    
        return (
            <Route
                {...rest}
                render={(routeProps) =>
                    (
                        <div>
                           ... // here is the auth header
                           <Component {...routeProps} />
                           .. // here is the auth footer
                        </div>
                    )
                }
            />
        );
    };
    
    export default AuthRoute;
    
    0 讨论(0)
  • 2020-12-15 02:05

    You could try having two different switch statements to handle your Auth and Protected routes. I had a similar use case at work and having two sets of switch blocks with only one running at one time was the way for me.

    const App: React.FC = () => {
        const history = createBrowserHistory();
    
        return (
            <div className="App">
                <Router history={history}>
                    {isLoggedIn ? <PrivateRoutes /> : <AuthRoutes />}
                </Router>
            </div>
        );
    };
    
    
    const PrivateRoutes: React.FC = () => {
        return (
            <>
                <Header />
                <Switch>
                    <Route path="/home" component={HomePage} />
                    <Route path="/dashboard" component={DashboardPage} />
                    <Route path="*" component={NotFoundPage} />
                </Switch>
                <Footer />
            </>
        );
    };
    
    const AuthRoutes: React.FC = () => {
        return (
            <>
                <Header />
                <Switch>
                    <Route path="/login" component={LoginPage} />
                    <Route path="/sign-up" component={SignUpPage} />
                    <Route path="*" component={NotFoundPage} />
                </Switch>
                <Footer />
            </>
        );
    };
    
    0 讨论(0)
提交回复
热议问题