React Router v5.0嵌套路由

时间:2019-06-22 00:15:36

标签: reactjs react-router

我正在构建一个React应用,但我无法使路由正常工作。

  1. 对于一些Auth路由(/loginsign-upforgot-password等,我需要一种通用的布局(页眉,页脚)

    < / li>
  2. 对于应用程序的其余受保护部分(HomeDashboard等),我需要另一种通用布局

  3. 我需要另一个没有任何布局的404页面。

我从这些链接中尝试了几种技术:

但是可以达到工作版本。

这是我目前拥有的:

(注意:目前,我忽略了将未登录的用户阻止到AppLayout的私有路由中的需要,我会在稍后处理)

const App: React.FC = () => {
    const history = createBrowserHistory();

    return (
        <div className="App">
            <Router history={history}>
                <Switch>
                    <AppLayout>
                        <Route path="/home" component={HomePage}/>
                        <Route path="/dashboard" component={DashboardPage}/>
                        ...
                    </AppLayout>
                    <AuthLayout>
                        <Route path="/login" component={LoginPage}/>
                        <Route path="/sign-up" component={SignUpPage}/>
                        ...
                    </AuthLayout>
                    <Route path="*" component={NotFoundPage} />
                </Switch>
            </Router>
        </div>
    );
};

export default App;

AuthLayoutAppLayout都很简单并且与此相似(只是每个页眉/页脚都不同):

class AppLayout extends Component {
    render() {
        return (
            <div className="AppLayout">
                <header>...</header>
                {this.props.children}
                <footer>...</footer>
            </div>
        );
    }
}

export default AppLayout;

问题是仅呈现来自AppLayout的路由。 其他路线仅显示AppLayout headerfooter,但没有任何内容。

这些是我正在使用的React版本:

    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.0",

任何帮助将不胜感激。

谢谢。

4 个答案:

答案 0 :(得分:2)

我花了一些时间找到自己喜欢的答案。 @ johnny-peter和@ gaurab-kc解决方案都很棒,它们使我对React的路由机制更加了解。

@ johnny-peter的解决方案具有迫使我在auth下(例如/auth/.../auth/login)下为所有auth/sign-up相关路由添加前缀的缺点。不想。

@ gaurab-kc解决方案仅支持一组路由..因此,如果用户已经注册,则他将无法再访问/login路由。

直到最近,我仍使用自己的解决方案,该解决方案的问题是“破坏了通用页眉和页脚的全部目的”。正如@ johnny-peter所提到的,它被否决了几次。

现在我正在使用另一种解决方案:

<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>

可防止上述所有缺陷。它允许我执行以下操作:

  1. 为每个部分使用一个布局,该布局不会在路线更改时重新呈现。
  2. 不强迫我在路由中添加任何前缀。
  3. 所有路由都同时工作,使用户无需先注销即可回到/login或其他auth路由。

此解决方案的唯一缺点是拥有更多代码并复制了路由,但这是我愿意支付的费用。

答案 1 :(得分:0)

您可以尝试使用两个不同的switch语句来处理您的Auth和Protected路由。我在工作时有一个类似的用例,而对我来说,有两组开关块只能一次运行。

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 />
        </>
    );
};

答案 2 :(得分:0)

每个布局都应具有一个路径组件,以区别于其他布局。

例如

Auth布局可以位于/auth下,例如,登录名将为/auth/login,注册名将为/auth/signup

应用布局可能会位于/app下,例如,仪表板将为/app/dashboard,主页将为/app/home

工作演示

Edit hungry-dubinsky-q1l62

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>
  );
}

如果您想保护某些未经身份验证的路由,也可以创建一个PrivateRoute组件,如果未经身份验证,该组件将重定向到身份验证布局。

PrivateRoute.js

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

您可以使用此PrivateRoute组件而不是react-router的{​​{1}}组件。

例如:

Route

答案 3 :(得分:-1)

@Gaurab Kc和@johnny peter的两种解决方案都很棒,尽管我最终做了这样的事情:

Column name price is ambiguous

<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> AuthRoute类似于:

PrivateRoute
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;