# Разделение доступа

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

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

## Разделение доступа

В данном разделе, по шагам будет разобран вход на сайт в качестве администратора. Следовательно, мы сделаем недоступным для посещения адрес `localhost:3000/admin`, а так же рассмотрим редирект на "главную" или в "админку" после ввода логина.

Создадим страницу логина.

В качестве "сервера для авторизации", будем использовать [localStorage](https://developer.mozilla.org/ru/docs/Web/API/Window/localStorage).

Алгоритм простой: вводится логин - кладется в localStorage.

*src/components/Login/index.js*

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

export default class Login extends Component {
  handleSubmit(e) {
    e.preventDefault()
    const value = e.target.elements[0].value
    window.localStorage.setItem('rr_login', value)
  }
  render() {
    return (
      <div className='row'>
        <div className='col-md-12'>Пожалуйста, введите логин:</div>
        <form className='col-md-4' onSubmit={this.handleSubmit}>
          <input type='text' placeholder='login'/>
          <button type='submit'>Войти</button>
        </form>
      </div>
    )
  }
}
```

*src/routes.js*

```javascript
...
import Login from './components/Login'
...
<Route path='/login' component={Login} />
...
```

*src/containers/App/index.js*

```javascript
...
<li><NavLink to='/login'>Войти</NavLink></li>
...
```

**Напоминаю**: несмотря на то, что ссылка появилась в шапке (hot-reload работает для компонента `<App />`), страницу все равно нужно перезагрузить (так как hot-reload не работает для обновления списка роутов).

![login\_max\_screen](/files/-LvLn_dZxlVXX9j0b2Ab)

Если ввести новый "логин" - старый перетирается. Данное допущение сейчас не мешает нам проверить работоспособность, а наоборот облегчает проверку.

Нам необходимо закрыть доступ "не админам", для этого потребуется разобрать "события", которые возникают в процессе изменений URL'a.

### onEnter, onLeave

*Предлагаю добавить слово "хук" в словарь. По-моему, хук, не что иное как "действие на событие".*

Итак, есть возможность использовать хуки на события onLeave и onEnter. По названию понятно: **onLeave** возникает, когда "роут покинут", а **onEnter** - в момент "захожу на роут".

Представьте адрес:

`react-site.com/profile/photos/`

его "роутер-реализацию": (не стоит так говорить в приличном месте)

`/ + profile + photos`

и его реализацию компонентами:

`<App /> + <Profile /> + <Photos />`

Представьте, что вы будучи на странице с фото, кликнули на ссылку для перехода на главную страницу. Произойдет:

* *onLeave* на `/profile/photos`
* *onLeave* на `/profile`
* *onEnter* на `/`

И обратная ситуация: вы находитесь на главной, и решили перейти в раздел фото:

* *onLeave* на `/`
* *onEnter* на `/profile`
* *onEnter* на `/profile/photos`

Вернемся к "хукам".

Добавьте в *routes.js* функцию *checkLogin* и непосредственно сам хук.

*src/routes.js*

```javascript
...
function checkLogin() {
  const login = window.localStorage.getItem('rr_login')
  if (login === 'admin') {
    console.log('пропусти')
  }
}
...
<Route path='/admin' component={Admin} onEnter={checkLogin}/>
...
```

Теперь если вы залогинетесь как **admin** - в консоли браузера вас "пропустят". Причем, заметьте, хук сработает при вводе url напрямую в строке ввода адреса + enter, либо если вы кликнете по ссылке. Так же, если вы уже находитесь в разделе admin и попробуете кликнуть на ссылку "Админка" - *onEnter* не произойдет, а следовательно и хук не сработает. Кажется, разработчики react-router'a постарались на славу.

![login\_admin\_screen](/files/-LvLn_dbW9n0SL7mQkqm)

*На всякий случай, напоминаю, что очистить localStorage можно командой* `localStorage.clear()`

У хука есть полезные аргументы - **nextState**, **replace**, **callback**. Нам понадобится replace. На пару слов подробнее можно прочитать в [офф.документации](https://github.com/reactjs/react-router/blob/master/docs/API.md#onenternextstate-replace-callback).

Перепишем функцию *checkLogin*

*src/routes.js*

```javascript
...
function checkLogin(nextState, replace) {
  const login = window.localStorage.getItem('rr_login')
  if (login !== 'admin') {
    replace('/')
  }
}
...
```

Попробуйте сейчас залогиниться под другим именем: вы не сможете войти на страницу /admin, независимо от того введете ли вы адрес и нажмете enter, или кликните по ссылке.

Страницу [API Reference](https://github.com/reactjs/react-router/blob/master/docs/API.md) настоятельно рекомендую добавить в закладки.

### Использование static method в качестве хука на onEnter

Наш код работает, но функция *checkLogin*, как будто мешается в файле с роутами. Может быть вынести ее в отдельный файл? А вы знаете, есть еще одно интересное решение: использовать [static метод](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Classes) класса Admin.

*src/components/Admin/index.js*

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

export default class Admin extends Component {
  static onEnter(nextState, replace) {
    const login = window.localStorage.getItem('rr_login')
    if (login !== 'admin') {
      replace('/')
    }
  }
  render() {
    return (
      <div className='row'>
        <div className='col-md-12'>Раздел /admin</div>
      </div>
    )
  }
}
```

Исправим routes.js (привожу полный листинг)

*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 Login from './components/Login'
import NotFound from './components/NotFound'

export const routes = (
  <div>
    <Route path='/' component={App}>
      <IndexRoute component={Home} />
      {/* в качестве хука на onEnter - статический метод класса Admin */}
      <Route path='/admin' component={Admin} onEnter={Admin.onEnter}/>
      <Route path='/genre/:genre' component={Genre}>
        <Route path='/genre/:genre/:release' component={Release} />
      </Route>
      <Route path='/list' component={List} />
      <Route path='/login' component={Login} />
    </Route>
    <Route path='*' component={NotFound} />
  </div>
)
```

Все, теперь с чистой совестью можете приступать к задачке на повторение.

**Задача**: если пользователь ввел admin - после нажатия кнопки "Войти" - направить его на /admin, иначе на /

Подсказка **#1**: Если данная задача вызвала у вас трудность, прочитайте еще раз [предыдущую главу](https://maxfarseer.gitbooks.io/react-router-course-ru/content/programmiruem_perehodi.html).

### Решение:

*src/components/Login/index.js*

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

export default class Login extends Component {
  constructor() {
    super()
    this.handleSubmit = this.handleSubmit.bind(this)
  }
  handleSubmit(e) {
    e.preventDefault()
    const login = e.target.elements[0].value
    window.localStorage.setItem('rr_login', login)

    if (login === 'admin') {
      this.context.router.push('/admin')
    } else {
      this.context.router.push('/')
    }
  }
  render() {
    return (
      <div className='row'>
        <div className='col-md-12'>Пожалуйста, введите логин:</div>
        <form className='col-md-4' onSubmit={this.handleSubmit}>
          <input type='text' placeholder='login'/>
          <button type='submit'>Войти</button>
        </form>
      </div>
    )
  }
}

Login.contextTypes = {
  router: PropTypes.object.isRequired
}
```

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

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


---

# 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/razdelenie_dostupa.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.
