Các tuyến lồng nhau với bộ định tuyến phản ứng v4 / v5


261

Tôi hiện đang vật lộn với các tuyến lồng nhau bằng bộ định tuyến v4.

Ví dụ gần nhất là cấu hình tuyến đường trong Tài liệu React-Router v4 .

Tôi muốn chia ứng dụng của tôi thành 2 phần khác nhau.

Một lối vào và một khu vực quản trị.

Tôi đã suy nghĩ về một cái gì đó như thế này:

<Match pattern="/" component={Frontpage}>
  <Match pattern="/home" component={HomePage} />
  <Match pattern="/about" component={AboutPage} />
</Match>
<Match pattern="/admin" component={Backend}>
  <Match pattern="/home" component={Dashboard} />
  <Match pattern="/users" component={UserPage} />
</Match>
<Miss component={NotFoundPage} />

Frontend có bố cục và phong cách khác với khu vực quản trị. Vì vậy, trong các frontpage tuyến đường về nhà, về và vì vậy một nên là các tuyến con.

/ home nên được kết xuất thành thành phần Frontpage và / admin / home sẽ được hiển thị trong thành phần Backend.

Tôi đã thử một số biến thể nhưng tôi luôn kết thúc bằng việc không nhấn / home hoặc / admin / home.

Chỉnh sửa - 19.04.2017

Bởi vì bài đăng này có rất nhiều lượt xem ngay bây giờ nên tôi đã cập nhật nó với giải pháp cuối cùng. Tôi hi vọng nó giúp ích cho ai đó.

Chỉnh sửa - 08.05.2017

Loại bỏ các giải pháp cũ

Giải pháp cuối cùng:

Đây là giải pháp cuối cùng tôi đang sử dụng ngay bây giờ. Ví dụ này cũng có thành phần lỗi toàn cầu như trang 404 truyền thống.

import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';

const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>

const Frontend = props => {
  console.log('Frontend');
  return (
    <div>
      <h2>Frontend</h2>
      <p><Link to="/">Root</Link></p>
      <p><Link to="/user">User</Link></p>
      <p><Link to="/admin">Backend</Link></p>
      <p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/' component={Home}/>
        <Route path='/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

const Backend = props => {
  console.log('Backend');
  return (
    <div>
      <h2>Backend</h2>
      <p><Link to="/admin">Root</Link></p>
      <p><Link to="/admin/user">User</Link></p>
      <p><Link to="/">Frontend</Link></p>
      <p><Link to="/admin/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
      <Switch>
        <Route exact path='/admin' component={Home}/>
        <Route path='/admin/user' component={User}/>
        <Redirect to={{
          state: { error: true }
        }} />
      </Switch>
      <footer>Bottom</footer>
    </div>
  );
}

class GlobalErrorSwitch extends Component {
  previousLocation = this.props.location

  componentWillUpdate(nextProps) {
    const { location } = this.props;

    if (nextProps.history.action !== 'POP'
      && (!location.state || !location.state.error)) {
        this.previousLocation = this.props.location
    };
  }

  render() {
    const { location } = this.props;
    const isError = !!(
      location.state &&
      location.state.error &&
      this.previousLocation !== location // not initial render
    )

    return (
      <div>
        {          
          isError
          ? <Route component={Error} />
          : <Switch location={isError ? this.previousLocation : location}>
              <Route path="/admin" component={Backend} />
              <Route path="/" component={Frontend} />
            </Switch>}
      </div>
    )
  }
}

class App extends Component {
  render() {
    return <Route component={GlobalErrorSwitch} />
  }
}

export default App;

1
Cảm ơn bạn đã cập nhật câu hỏi của bạn với câu trả lời cuối cùng! chỉ là một gợi ý: có lẽ bạn chỉ có thể giữ danh sách thứ 4 và danh sách đầu tiên, vì những người khác đang sử dụng các phiên bản lỗi thời của api và đang mất tập trung vào câu trả lời
Giuliano Vilela

lol, tôi không biết định dạng ngày này là gì: 08.05.2017 Tôi khuyên bạn nên sử dụng định dạng ISO8601 phổ quát cho các ngày nếu bạn không muốn gây nhầm lẫn cho mọi người. là 08 tháng hay ngày? ISO8601 = year.month.day giờ.minute.second (tăng dần chi tiết hơn)
wesm

Giải pháp cập nhật cuối cùng tốt đẹp, nhưng tôi nghĩ bạn không cần previousLocationlogic.
tudorpavel

động lực để viết lại hoàn toàn bộ định tuyến phản ứng. Đó là một lý do tốt
Oliver Watkins

Đó là cách tiếp cận khai báo. Vì vậy, bạn có thể thiết lập các tuyến đường của mình khi bạn sử dụng các thành phần phản ứng.
datoml

Câu trả lời:


318

Trong React-router-v4, bạn không làm tổ <Routes />. Thay vào đó, bạn đặt chúng bên trong một cái khác <Component />.


Ví dụ

<Route path='/topics' component={Topics}>
  <Route path='/topics/:topicId' component={Topic} />
</Route>

nên trở thành

<Route path='/topics' component={Topics} />

với

const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <Link to={`${match.url}/exampleTopicId`}>
      Example topic
    </Link>
    <Route path={`${match.path}/:topicId`} component={Topic}/>
  </div>
) 

Đây là một ví dụ cơ bản trực tiếp từ tài liệu về bộ định tuyến phản ứng .


Tôi có thể thực hiện từ liên kết của bạn trong ví dụ cơ bản tuy nhiên khi tôi nhập url theo cách thủ công thì nó không hoạt động trên máy chủ localhost của tôi. nhưng nó làm trên ví dụ của bạn. Mặt khác, HashRouter hoạt động chính xác khi tôi nhập url thủ công bằng #. Bạn có biết tại sao trên máy chủ localhost của tôi, BrowserRouter không hoạt động khi tôi gõ url theo cách thủ công không?
himawan_r

8
bạn có thể biến thành phần Chủ đề thành một lớp không? và tham số 'khớp' xuất hiện ở đâu? trong kết xuất ()?
user1076813

21
Có vẻ nực cười bạn không thể chỉ to="exampleTopicId"với việc ${match.url}được ngầm hiểu.
greenimpala

8
Bạn có thể có các tuyến đường lồng nhau trên mỗi tài liệu Reacttraining.com/react-router/web/example/route-config . Điều này sẽ cho phép cấu hình tuyến đường tập trung theo chủ đề trong tài liệu. Hãy nghĩ làm thế nào điên rồ này sẽ được quản lý trong một dự án lớn hơn nếu không có sẵn.
JustDave

5
Đây không phải là các tuyến được lồng, đó là định tuyến một cấp vẫn sử dụng thuộc tính kết xuất của Tuyến, lấy thành phần chức năng làm đầu vào, xem xét kỹ hơn, không có lồng nhau theo nghĩa của bộ định tuyến phản ứng <4. RouteWithSubRoutes là một -level danh sách các tuyến đường sử dụng khớp mẫu.
Lyubomir

102

react-router v6

Một bản cập nhật cho năm 2020: Bản phát hành v6 sắp tới sẽ có các Routethành phần lồng nhau mà Just Work ™. Xem mã ví dụ trong bài viết trên blog này .

Trong khi câu hỏi ban đầu là về v4 / v5 , câu trả lời đúng khi tàu v6 sẽ chỉ sử dụng nếu bạn có thể . Nó hiện đang ở giai đoạn alpha.


react-router v4 & v5

Đúng là để lồng các Tuyến, bạn cần đặt chúng vào thành phần con của Tuyến.

Tuy nhiên, nếu bạn thích một cú pháp nội tuyến hơn là phá vỡ các Tuyến của bạn trên các thành phần, bạn có thể cung cấp một thành phần chức năng cho chỗ dựa rendercủa Tuyến mà bạn muốn lồng vào.

<BrowserRouter>

  <Route path="/" component={Frontpage} exact />
  <Route path="/home" component={HomePage} />
  <Route path="/about" component={AboutPage} />

  <Route
    path="/admin"
    render={({ match: { url } }) => (
      <>
        <Route path={`${url}/`} component={Backend} exact />
        <Route path={`${url}/home`} component={Dashboard} />
        <Route path={`${url}/users`} component={UserPage} />
      </>
    )}
  />

</BrowserRouter>

Nếu bạn quan tâm đến lý do tại sao rendernên sử dụng componentprop chứ không phải prop, thì đó là vì nó ngăn thành phần chức năng nội tuyến không được hiển thị trên mỗi kết xuất. Xem tài liệu để biết thêm chi tiết.

Lưu ý rằng ví dụ này bao bọc các Tuyến được lồng trong một Đoạn . Trước React 16, bạn có thể sử dụng một container <div>thay thế.


16
Cảm ơn chúa, giải pháp duy nhất rõ ràng, dễ hiểu và hoạt động như mong đợi. Tôi muốn phản ứng bộ định tuyến 3 tuyến lồng nhau đã trở lại.
Merunas Grincala viêm

cái này hoàn hảo trông giống như lối ra góc cạnh
Partha Ranjan

4
Bạn nên sử dụng match.path, không match.url. Cái trước thường được sử dụng trong Tuyến pathchống đỡ; sau này khi bạn đẩy một tuyến đường mới (ví dụ: Liên kết tochống đỡ)
nbkhope

50

Chỉ muốn đề cập đến phản ứng-bộ định tuyến v4 đã thay đổi hoàn toàn kể từ khi câu hỏi này được đăng / trả lời.

Không còn <Match>thành phần nào nữa! <Switch>là để đảm bảo chỉ có trận đấu đầu tiên được kết xuất. <Redirect>tốt .. chuyển hướng đến một tuyến đường khác. Sử dụng hoặc bỏ qua exactđể trong hoặc loại trừ một phần khớp.

Xem tài liệu. Họ rất tuyệt. https://reacttraining.com/react-router/

Đây là một ví dụ tôi hy vọng có thể sử dụng để trả lời câu hỏi của bạn.

<Router>
  <div>
    <Redirect exact from='/' to='/front'/>
    <Route path="/" render={() => {
      return (
        <div>
          <h2>Home menu</h2>
          <Link to="/front">front</Link>
          <Link to="/back">back</Link>
        </div>
      );
    }} />          
    <Route path="/front" render={() => {
      return (
        <div>
        <h2>front menu</h2>
        <Link to="/front/help">help</Link>
        <Link to="/front/about">about</Link>
        </div>
      );
    }} />
    <Route exact path="/front/help" render={() => {
      return <h2>front help</h2>;
    }} />
    <Route exact path="/front/about" render={() => {
      return <h2>front about</h2>;
    }} />
    <Route path="/back" render={() => {
      return (
        <div>
        <h2>back menu</h2>
        <Link to="/back/help">help</Link>
        <Link to="/back/about">about</Link>
        </div>
      );
    }} />
    <Route exact path="/back/help" render={() => {
      return <h2>back help</h2>;
    }} />
    <Route exact path="/back/about" render={() => {
      return <h2>back about</h2>;
    }} />
  </div>
</Router>

Hy vọng nó sẽ giúp, cho tôi biết. Nếu ví dụ này không trả lời đủ câu hỏi của bạn, hãy cho tôi biết và tôi sẽ xem liệu tôi có thể sửa đổi nó không.


Không có exacttrên Redirect Reacttraining.com/react-router/web/api/Redirect Điều đó sẽ sạch hơn rất nhiều so với <Route exact path="/" render={() => <Redirect to="/path" />} />những gì tôi đang làm thay thế. Ít nhất là nó sẽ không cho phép tôi với TypeScript.
Filuren

2
Tôi có hiểu chính xác rằng không có những thứ như các tuyến lồng nhau / phụ (nữa) không? Tôi có cần phải nhân đôi tuyến cơ sở trong tất cả các tuyến không? Không phản ứng-bộ định tuyến 4 cung cấp bất kỳ trợ giúp để cấu trúc các tuyến theo cách duy trì?
Ville

5
@Ville Tôi ngạc nhiên; bạn đã tìm thấy một giải pháp tốt hơn? Tôi không muốn có các tuyến đường ở khắp mọi nơi, trời ạ
punkbit

1
điều này sẽ hoạt động nhưng đảm bảo rằng bạn có đường dẫn công khai được đặt thành "/" trong cấu hình webpack cho bundle.js, nếu không các tuyến được lồng sẽ không hoạt động khi làm mới trang.
vikrant

6

Một số thứ như thế này.

import React from 'react';
import {
  BrowserRouter as Router, Route, NavLink, Switch, Link
} from 'react-router-dom';

import '../assets/styles/App.css';

const Home = () =>
  <NormalNavLinks>
    <h1>HOME</h1>
  </NormalNavLinks>;
const About = () =>
  <NormalNavLinks>
    <h1>About</h1>
  </NormalNavLinks>;
const Help = () =>
  <NormalNavLinks>
    <h1>Help</h1>
  </NormalNavLinks>;

const AdminHome = () =>
  <AdminNavLinks>
    <h1>root</h1>
  </AdminNavLinks>;

const AdminAbout = () =>
  <AdminNavLinks>
    <h1>Admin about</h1>
  </AdminNavLinks>;

const AdminHelp = () =>
  <AdminNavLinks>
    <h1>Admin Help</h1>
  </AdminNavLinks>;


const AdminNavLinks = (props) => (
  <div>
    <h2>Admin Menu</h2>
    <NavLink exact to="/admin">Admin Home</NavLink>
    <NavLink to="/admin/help">Admin Help</NavLink>
    <NavLink to="/admin/about">Admin About</NavLink>
    <Link to="/">Home</Link>
    {props.children}
  </div>
);

const NormalNavLinks = (props) => (
  <div>
    <h2>Normal Menu</h2>
    <NavLink exact to="/">Home</NavLink>
    <NavLink to="/help">Help</NavLink>
    <NavLink to="/about">About</NavLink>
    <Link to="/admin">Admin</Link>
    {props.children}
  </div>
);

const App = () => (
  <Router>
    <div>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/help" component={Help}/>
        <Route path="/about" component={About}/>

        <Route exact path="/admin" component={AdminHome}/>
        <Route path="/admin/help" component={AdminHelp}/>
        <Route path="/admin/about" component={AdminAbout}/>
      </Switch>

    </div>
  </Router>
);


export default App;


6

Tôi đã thành công trong việc xác định các tuyến đường lồng nhau bằng cách gói Switchvà xác định tuyến đường lồng trước hơn tuyến đường gốc.

<BrowserRouter>
  <Switch>
    <Route path="/staffs/:id/edit" component={StaffEdit} />
    <Route path="/staffs/:id" component={StaffShow} />
    <Route path="/staffs" component={StaffIndex} />
  </Switch>
</BrowserRouter>

Tham khảo: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md


sắp xếp lại thứ tự đã khắc phục vấn đề của tôi mặc dù tôi không biết liệu điều này sẽ có bất kỳ tác dụng phụ nào không. nhưng làm việc ngay bây giờ .. cảm ơn :)
Anbu369

2

Bạn có thể thử một cái gì đó như Routes.js

import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom';
import FrontPage from './FrontPage';
import Dashboard from './Dashboard';
import AboutPage from './AboutPage';
import Backend from './Backend';
import Homepage from './Homepage';
import UserPage from './UserPage';
class Routes extends Component {
    render() {
        return (
            <div>
                <Route exact path="/" component={FrontPage} />
                <Route exact path="/home" component={Homepage} />
                <Route exact path="/about" component={AboutPage} />
                <Route exact path="/admin" component={Backend} />
                <Route exact path="/admin/home" component={Dashboard} />
                <Route exact path="/users" component={UserPage} />    
            </div>
        )
    }
}

export default Routes

App.js

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Routes from './Routes';

class App extends Component {
  render() {
    return (
      <div className="App">
      <Router>
        <Routes/>
      </Router>
      </div>
    );
  }
}

export default App;

Tôi nghĩ bạn cũng có thể đạt được điều tương tự từ đây.


điểm tốt! Tôi cũng nghĩ như vậy khi bắt đầu với React sau khi phát triển ứng dụng khởi động Java Spring. điều duy nhất tôi muốn thay đổi là 'div' thành 'Switch' trong Routes.js. Và tbh, bạn có thể xác định tất cả các tuyến đường trong App.js nhưng bao bọc bên ngoài trong tệp index.js chẳng hạn (tạo-Reac-app)
Reborn

Vâng, bạn đúng! Tôi đã thực hiện theo cách này đó là lý do tại sao tôi đề cập đến phương pháp này.
Aniruddh Agarwal

-6
interface IDefaultLayoutProps {
    children: React.ReactNode
}

const DefaultLayout: React.SFC<IDefaultLayoutProps> = ({children}) => {
    return (
        <div className="DefaultLayout">
            {children}
        </div>
    );
}


const LayoutRoute: React.SFC<IDefaultLayoutRouteProps & RouteProps> = ({component: Component, layout: Layout, ...rest}) => {
const handleRender = (matchProps: RouteComponentProps<{}, StaticContext>) => (
        <Layout>
            <Component {...matchProps} />
        </Layout>
    );

    return (
        <Route {...rest} render={handleRender}/>
    );
}

const ScreenRouter = () => (
    <BrowserRouter>
        <div>
            <Link to="/">Home</Link>
            <Link to="/counter">Counter</Link>
            <Switch>
                <LayoutRoute path="/" exact={true} layout={DefaultLayout} component={HomeScreen} />
                <LayoutRoute path="/counter" layout={DashboardLayout} component={CounterScreen} />
            </Switch>
        </div>
    </BrowserRouter>
);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.