เส้นทางที่ซ้อนกับ react v4 / v5 ของเราเตอร์


261

ฉันกำลังดิ้นรนกับเส้นทางการทำรังโดยใช้ react router v4

ตัวอย่างที่ใกล้เคียงที่สุดคือการตั้งค่าเส้นทางใน เอกสารตอบสนอง-Router v4

ฉันต้องการแยกแอปออกเป็นสองส่วน

ส่วนหน้าและพื้นที่ของผู้ดูแลระบบ

ฉันกำลังคิดเกี่ยวกับสิ่งนี้:

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

ส่วนหน้ามีเลย์เอาต์และสไตล์ที่แตกต่างจากพื้นที่ของผู้ดูแลระบบ ดังนั้นในหน้าแรกเส้นทางบ้านประมาณหนึ่งเส้นทางควรเป็นเส้นทางลูก

/ homeควรแสดงผลในองค์ประกอบ Frontpage และ/ admin / homeควรแสดงผลภายในองค์ประกอบ Backend

ฉันลองใช้รูปแบบต่าง ๆ แต่ฉันมักจะจบลงด้วยการไม่กดปุ่ม / home หรือ / admin / home

แก้ไข - 19.04.2017

เนื่องจากโพสต์นี้มีจำนวนการดูจำนวนมากในขณะนี้ฉันอัปเดตด้วยโซลูชันขั้นสุดท้าย ฉันหวังว่ามันจะช่วยให้ใครบางคน

แก้ไข - 08.05.2017

นำโซลูชันเก่าออก

ทางออกสุดท้าย:

นี่เป็นทางออกสุดท้ายที่ฉันใช้ตอนนี้ ตัวอย่างนี้ยังมีองค์ประกอบข้อผิดพลาดทั่วโลกเช่นหน้า 404 แบบดั้งเดิม

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
ขอบคุณสำหรับการอัปเดตคำถามของคุณพร้อมคำตอบสุดท้าย! เพียงข้อเสนอแนะ: บางทีคุณอาจเก็บเฉพาะรายชื่อที่ 4 และรายแรกเนื่องจากรายการอื่น ๆ กำลังใช้ api รุ่นที่ล้าสมัยและเบี่ยงเบนความสนใจจากคำตอบ
Giuliano Vilela

ฮ่า ๆ ฉันไม่รู้ว่ารูปแบบวันที่นี้คืออะไร: 08.05.2017 ฉันขอแนะนำให้คุณใช้รูปแบบ ISO8601 สากลสำหรับวันที่หากคุณไม่ต้องการสร้างความสับสนให้ผู้คน คือ 08 เดือนหรือวัน? ISO8601 = year.month.day hour.minute.second (มีความละเอียดมากขึ้นเรื่อย ๆ )
wesm

โซลูชันสุดท้ายที่ได้รับการปรับปรุงแล้วดี แต่ฉันคิดว่าคุณไม่จำเป็นต้องใช้previousLocationตรรกะ
tudorpavel

แรงจูงใจในการเขียนการตอบสนองของเราเตอร์คืออะไรอย่างสมบูรณ์ มันจะดีกว่าเป็นเหตุผลที่ดี
Oliver Watkins

มันเป็นวิธีการเปิดเผย ดังนั้นคุณสามารถตั้งค่าการกำหนดเส้นทางตามที่คุณจะใช้ส่วนประกอบการตอบสนอง
datoml

คำตอบ:


318

ในการตอบสนองเตอร์-v4 <Routes />คุณทำไม่ได้รัง <Component />แต่คุณใส่ไว้ภายในอื่น


ตัวอย่างเช่น

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

ควรกลายเป็น

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

กับ

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

นี่คือตัวอย่างพื้นฐานตรงจากตอบสนองเตอร์เอกสาร


ฉันสามารถนำไปใช้จากลิงก์ของคุณในตัวอย่างพื้นฐาน แต่เมื่อฉันพิมพ์ url ด้วยตนเองมันไม่ทำงานบนเซิร์ฟเวอร์ localhost ของฉัน แต่มันเป็นตัวอย่างของคุณ ในทางตรงกันข้าม HashRouter ทำงานอย่างถูกต้องเมื่อฉันพิมพ์ url manualy ด้วย # คุณมีความคิดว่าทำไมบนเซิร์ฟเวอร์ localhost ของฉัน BrowserRouter ไม่ทำงานเมื่อฉันพิมพ์ url ด้วยตนเอง?
himawan_r

8
คุณสามารถสร้างหัวข้อเป็นคลาสได้หรือไม่? และพารามิเตอร์ 'การจับคู่' เข้ามาอยู่ที่ไหน? ในเรนเดอร์ ()?
user1076813

21
ดูเหมือนไร้สาระที่คุณไม่สามารถทำได้เพียงแค่to="exampleTopicId"บอก${match.url}เป็นนัย
greenimpala

8
คุณสามารถมีเส้นทางที่ซ้อนกันต่อเอกสารreacttraining.com/react-router/web/example/route-config สิ่งนี้จะช่วยให้สามารถกำหนดค่าเส้นทางแบบรวมศูนย์ได้ตามหัวข้อในเอกสาร คิดว่าวิธีนี้บ้าจะจัดการในโครงการขนาดใหญ่ถ้าไม่พร้อมใช้งาน
JustDave

5
เส้นทางเหล่านี้ไม่ใช่เส้นทางซ้อนกัน แต่เป็นเส้นทางหนึ่งระดับที่ยังคงใช้คุณสมบัติการเรนเดอร์ของ Route ซึ่งใช้องค์ประกอบการทำงานเป็นอินพุตมองอย่างระมัดระวังมากขึ้นไม่มีการซ้อนในแง่ของเร้าเตอร์เร้าเตอร์ รายการระดับของเส้นทางที่ใช้การจับคู่รูปแบบ
Lyubomir

102

react-router v6

การอัปเดตในปี 2020: การเปิดตัวv6จะมีRouteส่วนประกอบที่ซ้อนกันที่ Just Work ™ ดูรหัสตัวอย่างในโพสต์บล็อกนี้

ขณะที่คำถามเดิมเป็นเรื่องเกี่ยวกับv4 / v5คำตอบที่ถูกต้องเมื่อv6เรือจะถูกใช้เพียงว่าถ้าคุณสามารถ ปัจจุบันอยู่ในอัลฟ่า


react-router v4 & v5

เป็นความจริงที่ว่าในการซ้อนเส้นทางเราจำเป็นต้องวางมันไว้ในองค์ประกอบลูกของเส้นทาง

อย่างไรก็ตามหากคุณต้องการไวยากรณ์แบบอินไลน์มากกว่าที่จะทำลาย Routes ของคุณข้ามส่วนประกอบคุณสามารถจัดเตรียมองค์ประกอบการทำงานให้กับrenderเสาของเส้นทางที่คุณต้องการซ้อน

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

หากคุณสนใจว่าเหตุใดจึงrenderควรใช้ไม้componentค้ำยันและไม่ใช้ไม้ค้ำยันนั่นเป็นเพราะมันจะหยุดองค์ประกอบการทำงานแบบอินไลน์ไม่ให้ถูกประกอบใหม่ในการเรนเดอร์ทุกครั้ง ดูเอกสารประกอบสำหรับรายละเอียดเพิ่มเติม

โปรดทราบว่าตัวอย่าง wraps เส้นทางที่ซ้อนกันในส่วน ก่อนที่จะตอบสนอง 16 คุณสามารถใช้คอนเทนเนอร์<div>แทน


16
ขอบคุณพระเจ้าทางออกเดียวที่ชัดเจน sortant mantainable และทำงานตามที่คาดไว้ ฉันหวังว่าเราเตอร์ตอบสนอง 3 เส้นทางซ้อนกันกลับมา
Merunas Grincalaitis

อันนี้ดูสมบูรณ์แบบเหมือนทางออกเชิงมุม
Partha Ranjan

4
คุณควรใช้ไม่ได้match.path match.urlอดีตมักจะใช้ในเส้นทางpathเสา; หลังเมื่อคุณกดเส้นทางใหม่ (เช่น Link toprop)
nbkhope

50

แค่อยากจะพูดถึง react-router v4 เปลี่ยนไปอย่างสิ้นเชิงเนื่องจากคำถามนี้ถูกโพสต์ / ตอบ

ไม่มี<Match>องค์ประกอบใด ๆ อีกต่อไป! <Switch>คือการตรวจสอบให้แน่ใจว่ามีการแสดงผลนัดแรกเท่านั้น <Redirect>ดี .. เปลี่ยนเส้นทางไปยังเส้นทางอื่น ใช้หรือไม่ใช้exactเพื่อเข้าร่วมหรือไม่รวมการแข่งขันบางส่วน

ดูเอกสาร พวกเขายอดเยี่ยม https://reacttraining.com/react-router/

นี่คือตัวอย่างที่ฉันหวังว่าจะเป็นประโยชน์ในการตอบคำถามของคุณ

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

หวังว่ามันจะช่วยให้ฉันรู้ หากตัวอย่างนี้ตอบคำถามของคุณไม่ดีพอบอกฉันแล้วฉันจะดูว่าฉันสามารถแก้ไขได้ไหม


ไม่มีexactบนRedirect reacttraining.com/react-router/web/api/Redirectนั่นจะสะอาดกว่า<Route exact path="/" render={() => <Redirect to="/path" />} />ที่ฉันทำอยู่แทน อย่างน้อยมันก็ไม่ให้ฉันกับ TypeScript
Filuren

2
ฉันเข้าใจอย่างถูกต้องหรือไม่ว่าไม่มีเส้นทางเช่นซ้อนกัน / เส้นทางย่อย (อีกต่อไป)? ฉันต้องทำซ้ำเส้นทางพื้นฐานในทุกเส้นทางหรือไม่ react-router 4 ไม่ได้ให้ความช่วยเหลือในการจัดเส้นทางในลักษณะที่สามารถบำรุงรักษาได้หรือไม่?
วิลล์

5
@Ville ฉันประหลาดใจ คุณพบทางออกที่ดีกว่าหรือไม่ ฉันไม่ต้องการมีเส้นทางไปทั่วสถานที่เอ้ย
พังก์

1
สิ่งนี้จะใช้งานได้ แต่ต้องแน่ใจว่าคุณได้ตั้งค่าเส้นทางสาธารณะเป็น "/" ในการกำหนดค่า webpack สำหรับ bundle.js มิฉะนั้นเส้นทางที่ซ้อนกันจะไม่ทำงานในการรีเฟรชหน้า
vikrant

6

บางสิ่งเช่นนี้

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

ฉันประสบความสำเร็จในการกำหนดเส้นทางแบบซ้อนโดยล้อมด้วยSwitchและกำหนดเส้นทางแบบซ้อนก่อนกว่าเส้นทางรูท

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

การอ้างอิง: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md


การเรียงลำดับใหม่แก้ไขปัญหาของฉันแม้ว่าฉันจะไม่รู้ว่าจะมีผลข้างเคียงหรือไม่ แต่ตอนนี้ทำงานได้แล้ว .. ขอบคุณ :)
Anbu369

2

คุณสามารถลองใช้ 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;

ฉันคิดว่าคุณสามารถบรรลุเป้าหมายเดียวกันได้จากที่นี่


จุดดี! ฉันคิดแบบเดียวกันเมื่อเริ่มต้นด้วย React หลังจากการพัฒนาแอพ Boot Java สิ่งเดียวที่ฉันเปลี่ยนคือ 'div' เป็น 'Switch' ใน Routes.js และ tbh คุณสามารถกำหนดเส้นทางทั้งหมดใน App.js แต่ล้อมไว้ด้านนอกในไฟล์ index.js เช่น (create-react-app)
Reborn

ใช่คุณถูก! ฉันใช้วิธีนี้ด้วยเหตุนี้ฉันจึงพูดถึงวิธีการนี้
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>
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.