# Дописываем роуты

*ОБНОВЛЕНИЕ 2018: в учебнике хорошая теория, но ему уже два года. Проверяйте версии пакетов. За выходом нового учебника можно следить в* [*telegram канале*](http://bit.ly/2FI9nhs) *или* [*twitter*](http://bit.ly/2FU6zRL)

*На* [*канале*](http://bit.ly/2FI9nhs) *так же проводятся бесплатные вебинары, публикуются переводы и авторские материалы,* [*присоединяйтесь*](http://bit.ly/2FI9nhs)*!*

## Дописываем роуты

Пока я писал этот курс, вышел react 15, давайте обновим сразу react и react-dom.

Далее в курсе будут использованы следующие версии:

```javascript
"react": "^15.0.1",
"react-dom": "^15.0.1",
```

Давайте вспомним, какие роуты мы хотели сделать:

```
/ - главная страница
/list - список жанров
/genre/:genre/ - список релизов данного жанра
/genre/:genre/:release - информация о релизе
/admin - страница администратора
```

Каждый раз, задача будет примерно такой:

* нужно создать компонент (и рендерить в нем детей, если есть дочерние url'ы)
* добавить компонент, как новый Route

Начнем с простого - роут, не имеющий детей - /list

*src/components/List.js*

```javascript
import React, { Component } from 'react'
import { Link } from 'react-router'

export default class List extends Component {
  render() {
    return (
      <div>
        <div className='row'>
          <div className='col-md-12'>
            <h3> Список жанров </h3>
          </div>
        </div>
        <div className='row'>
          <div className='col-md-12'>
            <ul className='list'>
              <li className='list__item'>
                <Link to='/genre/house/'>House</Link>
              </li>
              <li className='list__item'>
                <Link to='/genre/dnb/'>Drum and bass</Link>
              </li>
              <li className='list__item'>
                <Link to='/genre/hip-hop/'>Hip-hop</Link>
              </li>
            </ul>
          </div>
        </div>
      </div>
    )
  }
}
```

Обновим информацию о роутах: *src/index.js*

```javascript
...
import List from './components/List'
...

    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='admin' component={Admin} />
      <Route path='genre' component={Genre} />
      <Route path='list' component={List} />
    </Route>
...
```

*P.S. в данный момент у нас не поддерживается hot-reload для роутинга. Поэтому придется обновлять браузер по старинке.*

Будьте внимательны, я специально привожу не полный код, чтобы вы не страдали *copy-paste*.

`<List />` - дочерний компонент `<App />`, (посмотрите на URL - `/ + list` = две части, в переводе на react-router, это `<App />` + **this.props.children**, и конкретно в нашем случае это `<App />` + `<List />`)

Проверим в браузере:

![\[app + list screen\]](/files/-LvLn_gRWa8pMWMUps1B)

Обратите внимание, что у `<App />` - `childRoues: Array[3]`, именно этими детьми и являются `<Admin />`, `<Genre />` и `<List />` в данный момент. То есть, снова никакой магии.

Так же, обратите внимание сколько свойств "прокидывает" react-router. Это нам еще пригодится.

Кстати, если сейчас кликнуть по ссылке, например *House*, что отобразит браузер?

**Ответ** - отобразится компонент `<NotFound />`.

Покликав на ссылки, у нас вырисовываются следующие адреса:

```
http://localhost:3000/genre/house/
http://localhost:3000/genre/dnb/
http://localhost:3000/genre/hip-hop/
```

Для всех этих адресов, требуется отрисовывать один и тот же компонент, который будет отображать список релизов данного жанра. Как быть? Пора познакомиться с **динамическим** адресом.

### Динамический роут

Все очень просто. Сразу к делу. Изменим в *src/index.js*:

```javascript
<Route path='genre' component={Genre} />
```

на

```javascript
<Route path='genre/:genre' component={Genre} />
```

Обновите браузер:

![params\_house](/files/-LvLn_gTS_Y6ZTfjNcgR)

Отображается компонент `<Genre />`, при этом в **props** появилось свойство *params*, давайте сразу возьмем его в оборот:

*src/components/Genre.js*

```javascript
import React, { Component } from 'react'

export default class Genre extends Component {
  render() {
    return (
      <div className='row'>
        <h3 className='col-md-12'>{this.props.params.genre}</h3>
        <div className='col-md-12'>Здесь будет список релизов</div>
      </div>
    )
  }
}
```

![genre\_house\_screen](/files/-LvLn_gVmg3KV8x0NjSz)

Если попробовать ввести вместо *house* другое слово - все так же будет работать.

Будем называть динамическую часть url'a - **параметром**. То есть *:genre* - параметр.

Нет никакого ограничения на количество параметров в адресе. Например:

`localhost:3000/:param_one/not_param/:param_two/:param_three`

**Задача**: создать компонент , который будет доступен по адресу:

`localhost:3000/genre/house/tiesto-the-only-way-is-up`

и будет выглядеть так:

![tiesto\_only\_way](/files/-LvLn_gXS2MoqcxCtRC5)

**Подсказка #1**: Необходимо использовать *nesting* (вложения), а значит - потребуется рендерить `this.props.children`

**Решение**:

*src/index.js*

```
...

import Release from './components/Release'

...
  <Router history={browserHistory}>
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='admin' component={Admin} />
      <Route path='genre/:genre' component={Genre}>
           {/* добавили новый route */}
        <Route path=':release' component={Release} />
      </Route>
      <Route path='list' component={List} />
    </Route>
    <Route path='*' component={NotFound} />
  </Router>,
...
```

*src/components/Genre.js*

```javascript
import React, { Component } from 'react'

export default class Genre extends Component {
  render() {
    let template;
    {/* если параметр release есть - покажи дочерний компонент */}
    if (this.props.params.release) {
      template = (
        <div className='row'>
          <h3 className='col-md-12'>{this.props.params.genre}</h3>
          <div className='col-md-12'>{this.props.children}</div>
        </div>
      )
    } else {
      template = (
        <div className='row'>
          <h3 className='col-md-12'>{this.props.params.genre}</h3>
          <div className='col-md-12'>Здесь будет список релизов</div>
        </div>
      )
    }

    return template;
  }
}
```

*src/components/Release.js*

```javascript
import React, { Component } from 'react'

export default class Release extends Component {
  render() {
    {/* замени все '-' в параметре (то есть в адресе) на пробелы */}
    const releaseName = this.props.params.release.replace(/-/g,' ');
    return (
      <div className='col-md-12'>
        {releaseName}
      </div>
    )
  }
}
```

С параметром мы можем работать как с любым другим свойством.

### Оформление routes

Чем глубже уровень вложение в роутах, тем сложнее сразу понять какому роуту соответствует тот или иной компонент. Поэтому, мы можем указать путь полностью:

```javascript
<Route path='/' component={App}>
  <IndexRoute component={Home} />
  <Route path='/admin' component={Admin} />
  <Route path='/genre/:genre' component={Genre}>
    <Route path='/genre/:genre/:release' component={Release} />
  </Route>
  <Route path='/list' component={List} />
</Route>
```

Так же, давайте вынесем *routes* в отдельный файл.

*src/routes.js*

```javascript
import React from 'react'
import { Route, IndexRoute } from 'react-router'

import App from './containers/App'
import Admin from './components/Admin'
import List from './components/List'
import Genre from './components/Genre'
import Release from './components/Release'
import Home from './components/Home'
import NotFound from './components/NotFound'

export const routes = (
  <div>
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      <Route path='/admin' component={Admin} />
      <Route path='/genre/:genre' component={Genre}>
        <Route path='/genre/:genre/:release' component={Release} />
      </Route>
      <Route path='/list' component={List} />
    </Route>
    <Route path='*' component={NotFound} />
  </div>
)
```

Так как *routes* - реакт компонент, и мы используем `<NotFound />` не в качестве дочернего компонента (чтобы не было "шапки"), нам пришлось обернуть роуты в `<div>`. Вы можете сделать иначе, если хотите, чтобы шапка (сейчас это ссылки Admin и Genre) была на всех страницах, включая 404.

Обновим код для подключения роутов:

*src/index.js*

```javascript
import 'babel-polyfill'
import React from 'react'
import { render } from 'react-dom'
import { Router, browserHistory } from 'react-router'
import { routes } from './routes'

render(
  <Router history={browserHistory} routes={routes} />,
  document.getElementById('root')
)
```

### Приберемся

У нас появилась нерабочая ссылка в шапке - Genre. Так как она нам больше не нужна, давайте заменим ее на 'Список жанров' и будем отображать компонент `<List />` по клику на эту ссылку.

Заодно переименуем Admin. Удалим заголовок App и накинем парочку bootstrap классов тэгу `<ul>`.

*src/containers/App.js*

```javascript
import React, { Component } from 'react'
import { Link } from 'react-router'

export default class App extends Component {
  render() {
    return (
      <div className='container'>
        <ul className='nav nav-pills'>
          <li><Link to='/admin'>Админка</Link></li>
          <li><Link to='/list'>Список жанров</Link></li>
        </ul>
        {this.props.children}
      </div>
    )
  }
}
```

Проверьте в браузере, покликайте по ссылкам. Понажимайте вперед/назад.

**Итого**: мы рассмотрели как можно создать динамический роут, и как можно использовать динамическую часть в шаблоне компонента.

[Исходный код](https://github.com/maxfarseer/react-router-ru-tutorial/tree/edit_routes_v2) на данный момент (комментарии удалены)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://max-frontend.gitbook.io/react-router-course-ru/podklyuchaem_react-router/dopisivaem_routi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
