Вкладені маршрути з реактором маршрутизатора v4 / v5


261

В даний час я борюся з гніздовими маршрутами, використовуючи реакційний маршрутизатор v4.

Найближчим прикладом була конфігурація маршруту в Документації React-Router v4 .

Я хочу розділити свою програму на 2 різні частини.

Зовнішній вигляд та адміністратор.

Я думав про щось подібне:

<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
Дякуємо, що оновили своє запитання остаточною відповіддю! лише пропозиція: можливо, ви могли б зберегти лише четвертий список і перший, оскільки інші використовують застарілі версії api і відволікають на відповідь
Джуліано Вілела

хаха, я не маю поняття, який формат цієї дати: 08.05.2017 Я пропоную вам використовувати універсальний формат ISO8601 для дат, якщо ви не хочете плутати людей. 08 місяць чи день? ISO8601 = year.month.day hour.minute.second (прогресивно більш деталізований)
wesm

Гарне оновлене остаточне рішення, але я думаю, що вам не потрібна previousLocationлогіка.
tudorpavel

яка мотивація повністю переписати маршрутизатор реагування. Краще бути вагомою причиною
Олівер Уоткінс

Це декларативний підхід. Таким чином, ви можете налаштувати маршрутизацію так, як використовували б реагуючі компоненти.
datoml

Відповіді:


318

У react-router-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-адресу вручну за допомогою #. Чи маєте ви ідею, чому на моєму сервері localhost BrowserRouter не працює, коли я ввожу URL вручну?
himawan_r

8
чи можна перетворити компонент Topics у клас? а де прийшов параметр 'match'? в render ()?
користувач1076813

21
Здається смішним, що ти не можеш просто to="exampleTopicId"з ${match.url}істотою неявною.
greenimpala

8
Ви можете мати вкладені маршрути на кожен документ reacttraining.com/react-router/web/example/route-config . Це дозволило б виконати централізовану конфігурацію маршруту відповідно до теми в документах. Подумайте, наскільки безумним було б це управління в масштабнішому проекті, якщо його немає.
JustDave

5
Це не вкладені маршрути, це однорівнева маршрутизація, яка все ще використовує властивість візуалізації маршруту, яка приймає функціональний компонент як вхід, дивіться уважніше, немає гніздування в сенсі реакційного маршрутизатора <4. RouteWithSubRoutes - це один -рівень-список маршрутів, які використовують відповідність шаблонів.
Любомир

102

react-router v6

Оновлення до 2020 року: майбутня версія v6 матиме вкладені Routeкомпоненти, які Just Work ™. Дивіться приклад коду в цій публікації в блозі .

Хоча в оригінальному запитанні йдеться про v4 / v5 , правильна відповідь, коли кораблі v6 будуть просто використовувати це, якщо зможете . Наразі це в альфа.


react-router v4 & v5

Це правда, що для гніздування маршрутів потрібно розмістити їх у дочірніх компонентах маршруту.

Однак якщо ви віддаєте перевагу більш вбудований синтаксис, а не розбивати маршрути на різні компоненти, ви можете надати функціональний компонент 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опору, це тому, що вона зупиняє вбудований функціональний компонент від перерахунку на кожному візуалізації. Детальніше дивіться в документації .

Зауважте, що приклад обгортає вкладені маршрути у фрагмент . До Реагу 16 ви можете використовувати контейнер <div>замість цього.


16
Слава Богу, єдине рішення, яке є ясним, зрозумілим та ефективним та працює як очікувалося. Я хотів би реагувати маршрутизатором 3 вкладені маршрути повернулися.
Merunas Grincalaitis

це один прекрасний зовнішній вигляд , як кутова маршрутної розетка
Парта Ranjan

4
Ви повинні використовувати match.path, не match.url. Перший зазвичай використовується в pathопорі Route ; останнє, коли ви натискаєте новий маршрут (наприклад, toопора посилання )
nbkhope

50

Просто хотілося відзначити, що реактор-роутер 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>

Сподіваюся, що це допомогло, дайте мені знати. Якщо цей приклад недостатньо добре відповідає на ваше запитання, скажіть мені, і я побачу, чи зможу я його змінити.


На reacttraining.com/react-router/web/api/Redirect немає exactна Redirect сайті. Це було б набагато чистіше, ніж <Route exact path="/" render={() => <Redirect to="/path" />} />те, що я роблю замість цього. Принаймні, це не дозволить мені з TypeScript.
Filuren

2
Чи правильно я розумію, що немає такого поняття, як вкладені / під маршрути (вже)? чи потрібно дублювати базовий маршрут у всіх маршрутах? Чи не реагує маршрутизатор 4 не допомагає структурувати маршрути в релевантному порядку?
Віль

5
@Ville Я здивований; чи знайшли ви краще рішення? Я не хочу мати маршрути всюди,
боже

1
це буде працювати, але переконайтеся, що у налаштуваннях веб-упаковки для 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та визначивши вкладений маршрут раніше, ніж root.

<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 після завантаження Java Spring. Єдине, що я міняю - це "div" на "Switch" в Routes.js. І tbh, ви можете визначити всі маршрути в App.js, але перегорнути його назовні у файл index.js, наприклад (create-react-app)
Відроджуватися

Так, ти маєш рацію! Я реалізував цей спосіб, тому я згадую цей метод.
Анірудд Агарвал

-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>
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.