Вы читаете заголовок и понимаете, зачем вы здесь! Хотите сделать защищенные маршруты реагирования в 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
}
}
Надеюсь, все это было вам полезно. Хорошего дня и спасибо за чтение!