Rute bersarang dengan router reaksi v4 / v5

261

Saat ini saya berjuang dengan rute bersarang menggunakan router reaksi v4.

Contoh terdekat adalah konfigurasi rute dalam Dokumentasi React-Router v4 .

Saya ingin membagi aplikasi menjadi 2 bagian berbeda.

Frontend dan area admin.

Saya sedang memikirkan sesuatu seperti ini:

<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 memiliki tata letak dan gaya yang berbeda dari area admin. Jadi di dalam halaman depan rute pulang, sekitar dan jadi orang harus menjadi rute anak.

/ home harus dirender ke dalam komponen Frontpage dan / admin / home harus di-render dalam komponen Backend.

Saya mencoba beberapa variasi tetapi saya selalu berakhir dengan tidak memukul / home atau / admin / home.

Edit - 19.04.2017

Karena posting ini memiliki banyak pandangan sekarang saya memperbaruinya dengan solusi akhir. Saya harap ini membantu seseorang.

Edit - 08.05.2017

Solusi lama yang dihapus

Solusi akhir:

Ini adalah solusi terakhir yang saya gunakan saat ini. Contoh ini juga memiliki komponen kesalahan global seperti halaman 404 tradisional.

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;
datoml
sumber
1
Terima kasih telah memperbarui pertanyaan Anda dengan jawaban akhir! hanya sebuah saran: mungkin Anda hanya dapat menyimpan daftar ke-4 dan yang pertama, karena yang lain menggunakan versi api yang sudah ketinggalan zaman dan mengalihkan perhatian dari jawabannya
Giuliano Vilela
lol, saya tidak tahu format apa tanggal ini: 08.05.2017 Saya sarankan Anda menggunakan format universal ISO8601 untuk tanggal jika Anda tidak ingin membingungkan orang. adalah 08 bulan atau hari? ISO8601 = year.month.day hour.minute.second (semakin granular)
wesm
Solusi akhir yang bagus diperbarui, tapi saya pikir Anda tidak perlu previousLocationlogika.
tudorpavel
apa motivasi untuk sepenuhnya menulis ulang router reaksi. Lebih baik menjadi alasan yang bagus
Oliver Watkins
Ini pendekatan deklaratif. Jadi, Anda dapat mengatur perutean seperti menggunakan komponen reaksi.
datoml

Jawaban:

318

Di react-router-v4 Anda tidak bersarang <Routes />. Sebaliknya, Anda menempatkan mereka di dalam yang lain <Component />.


Misalnya

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

harus menjadi

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

dengan

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

Berikut ini adalah contoh dasar langsung dari dokumentasi router reaksi .

Lyubomir
sumber
Saya dapat menerapkan dari tautan Anda dalam contoh dasar namun ketika saya mengetik url secara manual itu tidak berfungsi pada server localhost saya. tetapi itu pada contoh Anda. Di sisi lain HashRouter berfungsi dengan baik ketika saya mengetik url secara manual dengan #. Apakah Anda tahu mengapa di server localhost saya BrowserRouter tidak berfungsi ketika saya mengetik url secara manual?
himawan_r
8
dapatkah Anda membuat komponen Topik menjadi kelas? dan di mana parameter 'kecocokan' masuk? dalam render ()?
user1076813
21
Tampak konyol Anda tidak bisa hanya to="exampleTopicId"dengan ${match.url}yang tersirat.
greenimpala
8
Anda dapat memiliki rute yang disarangkan per dokumen reacttraining.com/react-router/web/example/route-config . Ini akan memungkinkan konfigurasi rute terpusat sesuai dengan topik dalam dokumen. Pikirkan betapa gilanya hal ini untuk dikelola dalam proyek yang lebih besar jika tidak tersedia.
JustDave
5
Ini bukan rute bersarang, ini adalah routing satu tingkat yang masih menggunakan properti render dari Route yang mengambil komponen fungsional sebagai input, lihat lebih hati-hati, tidak ada sarang dalam arti router reaksi <4. RouteWithSubRoutes adalah satu daftar tingkat -level yang menggunakan pencocokan pola.
Lyubomir
102

react-router v6

Pembaruan untuk 2020: Rilis v6 yang akan datang akan memiliki Routekomponen bersarang yang Just Work ™. Lihat contoh kode di posting blog ini .

Sementara pertanyaan aslinya adalah tentang v4 / v5 , jawaban yang tepat ketika v6 dikirimkan hanya akan digunakan jika Anda bisa . Saat ini dalam alpha.


react-router v4 & v5

Memang benar bahwa untuk membuat Rute, Anda perlu menempatkannya di komponen turunan dari Rute.

Namun, jika Anda lebih memilih sintaks sebaris daripada memecah Rute Anda di seluruh komponen, Anda dapat memberikan komponen fungsional untuk renderpenyangga Rute yang ingin Anda masukkan .

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

Jika Anda tertarik mengapa renderprop harus digunakan, dan bukan componentprop, itu karena menghentikan komponen fungsional sebaris yang di-remount pada setiap render. Lihat dokumentasi untuk lebih detail.

Perhatikan bahwa contoh membungkus Routes bersarang di Fragmen . Sebelum Bereaksi 16, Anda bisa menggunakan wadah <div>.

davnicwil
sumber
16
Terima kasih Tuhan, satu-satunya solusi yang jelas, dapat dipertahankan dan berfungsi seperti yang diharapkan. Saya berharap router bereaksi 3 rute bersarang kembali.
Merunas Grincalaitis
yang sempurna ini tampak seperti outlet-rute bersudut
Partha Ranjan
4
Anda harus menggunakan match.path, bukan match.url. Yang pertama biasanya digunakan dalam pathpenyangga Rute ; yang terakhir ketika Anda mendorong rute baru (mis. Tautan toprop)
nbkhope
50

Hanya ingin menyebutkan react-router v4 berubah secara radikal sejak pertanyaan ini diposting / dijawab.

Tidak ada <Match>komponen lagi! <Switch>adalah untuk memastikan hanya pertandingan pertama yang diberikan. <Redirect>yah .. dialihkan ke rute lain. Gunakan atau tinggalkan exactuntuk masuk atau mengecualikan kecocokan sebagian.

Lihat dokumen. Mereka itu hebat. https://reacttraining.com/react-router/

Berikut ini contoh yang saya harap bisa digunakan untuk menjawab pertanyaan Anda.

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

Semoga ini bisa membantu, beri tahu saya. Jika contoh ini tidak menjawab pertanyaan Anda dengan cukup baik, beri tahu saya dan saya akan melihat apakah saya dapat memodifikasinya.

jar0m1r
sumber
Tidak ada exactdi Redirect reacttraining.com/react-router/web/api/Redirect Itu akan jauh lebih bersih daripada <Route exact path="/" render={() => <Redirect to="/path" />} />yang saya lakukan sebagai gantinya. Setidaknya itu tidak akan membiarkan saya dengan TypeScript.
Filuren
2
Apakah saya mengerti benar bahwa tidak ada yang namanya nested / sub rute (lagi)? apakah saya perlu menduplikasi rute dasar di semua rute? Bukankah react-router 4 menyediakan bantuan untuk menyusun rute dengan cara yang bisa dipertahankan?
Ville
5
@Ville Saya heran; apakah Anda menemukan solusi yang lebih baik? Saya tidak ingin memiliki rute di semua tempat, astaga
punkbit
1
ini akan bekerja tetapi pastikan Anda memiliki jalur publik yang disetel ke "/" di konfigurasi webpack untuk bundle.js, jika tidak, rute yang disarangkan tidak akan berfungsi pada penyegaran halaman.
vikrant
6

Sesuatu seperti ini.

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;

Sanjeev Shakya
sumber
6

Saya berhasil mendefinisikan rute bersarang dengan membungkus dengan Switchdan menentukan rute bersarang sebelum dari rute root.

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

Referensi: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md

asukiaaa
sumber
mengatur ulang pesanan memperbaiki masalah saya meskipun saya tidak tahu apakah ini akan memiliki efek samping. tetapi bekerja untuk sekarang .. terima kasih :)
Anbu369
2

Anda dapat mencoba sesuatu seperti 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;

Saya pikir Anda dapat mencapai hal yang sama dari sini juga.

Aniruddh Agarwal
sumber
poin bagus! Saya memikirkan hal yang sama ketika mulai dengan React setelah pengembangan aplikasi boot Java Spring. satu-satunya hal yang saya ubah adalah 'div' ke 'Switch' di Routes.js. Dan tbh, Anda dapat mendefinisikan semua rute di App.js tetapi membungkusnya di luar dalam file index.js misalnya (create-react-app)
Reborn
Ya kamu benar! Saya telah menerapkan cara ini, itulah mengapa saya menyebutkan metode ini.
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>
);
BAHKAN GLUKHOV
sumber