Вы читаете заголовок и понимаете, зачем вы здесь! Хотите сделать защищенные маршруты реагирования в 2020 году? Пойдем! (Вы должны быть знакомы с маршрутизатором, чтобы следовать этому руководству)

Сначала создайте проект реакции. Мне нравится отделять свой маршрутизатор от файла app.js в отдельный файл router.js. Мой файл app.js выглядит так:

//App.js
import './App.css'
import React from 'react'
import Router from './components/application/router.js'
export default function App() {
  return (
    <div className='App'>
      <Router />
    </div>
  )
}

Правильно, я не использую точки с запятой; кроме моего английского.

Давайте настроим файл router.js:

//router.js
//pretend we imported all of our components
import React from 'react'
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
export default function Router() {
  return (
      <BrowserRouter>
        <Navigation /> 
        // I like to put my nav links into a seperate file
          <Switch>
            <Route exact path='/'>
              <PublicComponent />
            </Route>
          </Switch>
      </BrowserRouter>
  )
}

Здесь у нас есть простой маршрутизатор с одним маршрутом, корневой маршрут является общедоступным. Чтобы добавить аутентификацию к нашим частным маршрутам, которые скоро будут созданы, нам нужно создать контекст для нашего маршрутизатора. Если вы будете следовать этому руководству, вам не нужно сильное понимание контекста, но вот официальное описание:

«Контекст предоставляет способ передавать данные через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне».

Пожалуйста, прочтите Контекст - React (reactjs.org) для более подробной информации.

Итак, мы собираемся начать с создания контекста и ловушки, о которых вы можете прочитать здесь: Знакомство с хуками - React (reactjs.org).

//hooks/auth.js
import React, { useState, useEffect, useContext, createContext } from 'react'
const authContext = createContext()
export function ProvideAuth({ children }) {
  const auth = useProvideAuth()
  return <authContext.Provider value={auth}> { children } </authContext.Provider>
}
export const useAuth = () => useContext(authContext)
function useProvideAuth() {
  return []
}

Это наш контекст и ловушка. Давайте поговорим о том, что у нас есть (подсказка: немного).

После импорта необходимых требований создаем контекст. Затем мы экспортируем наш контекст как компонент. Не беспокойтесь слишком сильно о том, как все это работает, это выходит за рамки нашей компетенции. На самом деле, наиболее важно отметить, что все, что возвращает useProvideAuth(), теперь доступно с помощью ловушки useAuth().

Имея это в виду, давайте создадим полезное возвращаемое значение!

//hooks/auth.js
//we need a base URL for our sign in function
const baseUrl = process.env.REACT_APP_BASE_URL
***
function useProvideAuth() {
  const [user, setUser] = useState(null)
  const signin = (credentials) => {
    fetch(baseUrl + 'login', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'Accepts': 'application/json'
      },
      body: JSON.stringify(credentials)
    })
    .then(res => res.json())
    .then(json => setUser(json.user))
  }
useEffect(() => {
    //check authentication
  }, [])
return {
    user,
    signin
  }
}

Мы использовали useState() функциональность React, чтобы установить константу с именем user и создать функцию, которая позволит нам обновить ее. Как вы можете видеть во втором .then() после нашего fetch() у нас есть setUser(json.user).

Если вы хотите включить функцию проверки, вошел ли пользователь в систему при посещении сайта, это следует сделать в обратном вызове useEffect().

Вернемся к маршрутизатору, давайте воспользуемся вновь созданным контекстом:

//router.js
***
export default function Router() {
  return (
    <ProvideAuth>
      <BrowserRouter>
        <Navigation /> 
        // I like to put my nav links into a seperate file
          <Switch>
            <Route exact path='/'>
              <PublicComponent />
            </Route>
          </Switch>
      </BrowserRouter>
    </ProvideAuth>
  )
}

Что дальше? Как мы используем значение нашей переменной аутентификации в контексте? Создадим частный маршрут. Если бы вы не знали, что можете создавать причудливые пользовательские маршруты, когда захотите - что ж, ваш ум вот-вот взорвется!

//router.js
***
function PrivateRoute({ children, ...rest }) {
  const auth = useAuth()
  return (
    <Route {...rest} render={({ location }) =>
        auth.user ? (children) :
        (<Redirect to={{ pathname: '/login', state: { from: location } }} />)
      }
    />
  )
}

Маршруты - это просто компоненты! Первым аргументом всегда являются дочерние элементы маршрута. ...rest - это просто остальные параметры в теге маршрута (если есть). Как видите, мы инициализируем наш хук useAuth().

В нашем возвращаемом значении мы просто берем стандартный компонент маршрута React и даем ему ...rest в параметре рендеринга, мы передаем прекрасную функцию, которая принимает местоположение в качестве аргумента. Эта функция возвращает троичный оператор «if», который проверяет значение auth.user и возвращает либо компонент, вложенный между <PrivateRoute></PrivateRoute>, либо перенаправляет нас на маршрут входа в систему.

Вернувшись в наш маршрутизатор, мы можем реализовать PrivateRoute:

//router.js
***
export default function Router() {
  return (
    <ProvideAuth>
      <BrowserRouter>
        <Navigation /> 
        // I like to put my nav links into a seperate file
          <Switch>
            <Route exact path='/'>
              <PublicComponent />
            </Route>
            <PrivateRoute path='private'>
               <PrivateComponent />
            </PrivateRoute>
          </Switch>
      </BrowserRouter>
    </ProvideAuth>
  )
}

Да, это так просто! Но мы еще не закончили.

В нашем компоненте входа в систему (с этим вы можете работать самостоятельно) нам необходимо включить ссылку на нашу функцию входа в систему.

//hypotheticalLoginComponent.js
const auth = useAuth()
*** 
const onSubmit = () => {
 auth.signin(credentials)
}

В компоненте входа в систему вы должны использовать useAuth(), чтобы отправить учетные данные для входа в наш замечательный обработчик аутентификации.

Если вам нравится абстрагировать все вызовы API в другой файл, вы также можете просто вызвать signin() функцию setUser() и вызвать эту функцию входа после разрешения вызова API.

Вот последние файлы для router.js и auth.js

//router.js
import React from 'react'
import { BrowserRouter, Switch, Route, Redirect } from 'react-router-dom'
import { ProvideAuth, useAuth } from '../../hooks/auth.js'
export default function Router() {
  return (
    <ProvideAuth>
      <BrowserRouter>
        <Navigation /> 
        // I like to put my nav links into a seperate file
          <Switch>
            <Route exact path='/'>
              <PublicComponent />
            </Route>
            <PrivateRoute path='private'>
               <PrivateComponent />
            </PrivateRoute>
          </Switch>
      </BrowserRouter>
    </ProvideAuth>
  )
}
function PrivateRoute({ children, ...rest }) {
  const auth = useAuth()
  return (
    <Route {...rest} render={({ location }) =>
        auth.user ? (children) :
        (<Redirect to={{ pathname: '/login', state: { from: location } }} />)
      }
    />
  )
}
import React, { useState, useEffect, useContext, createContext } from 'react'
const baseUrl = process.env.REACT_APP_BASE_URL
const authContext = createContext()
export function ProvideAuth({ children }) {
  const auth = useProvideAuth()
  return <authContext.Provider value={auth}> { children } </authContext.Provider>
}
export const useAuth = () => useContext(authContext)
function useProvideAuth() {
  const [user, setUser] = useState(null)
const signin = (credentials) => {
    fetch(baseUrl + 'login', {
      method: 'POST',
      credentials: 'include',
      headers: {
        'Content-Type': 'application/json',
        'Accepts': 'application/json'
      },
      body: JSON.stringify(credentials)
    })
    .then(res => res.json())
    .then(json => setUser(json.user))
  }
useEffect(() => {
    //check authentication
  }, [])
return {
    user,
    signin
  }
}

Надеюсь, все это было вам полезно. Хорошего дня и спасибо за чтение!