Магазин на JavaScript, часть 10 из 19. Маршруты, панель навигации, контекст приложения

16.12.2021

Теги: BackendExpress.jsFrontendJavaScriptNode.jsORMReact.jsWeb-разработкаБазаДанныхИнтернетМагазинКаталогТоваровКорзинаФреймворк

Маршруты приложения

В React имеется своя система маршрутизации, которая позволяет сопоставлять запросы к приложению с определенными компонентами. Ключевым звеном в работе маршрутизации является модуль react-router, который состоит из react-router-dom и react-router-native. Для браузерных приложений предназначен модуль react-router-dom, который мы уже установили. Добавляем маршруты приложения в файл App.js:

import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Shop from './pages/Shop.js'
import Product from './pages/Product.js'
import Basket from './pages/Basket.js'
import Login from './pages/Login.js'
import Signup from './pages/Signup.js'
import Delivery from './pages/Delivery.js'
import Contacts from './pages/Contacts.js'
import NotFound from './pages/NotFound.js'

const App = () => {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Shop />} />
                <Route path="/product/:id" element={<Product />} />
                <Route path="/basket" element={<Basket />} />
                <Route path="/login" element={<Login />} />
                <Route path="/signup" element={<Signup />} />
                <Route path="/delivery" element={<Delivery />} />
                <Route path="/contacts" element={<Contacts />} />
                <Route path="*" element={<NotFound />} />
            </Routes>
        </BrowserRouter>
    )
}

export default App
Почитать о маршрутизации с использованием React Router v6 можно здесь.

Но здесь далеко не все маршруты. Давайте прикинем, какие вообще маршруты могут быть. Во-первых, public-маршруты, которые доступны всем посетителям. Во-вторых, auth-маршруты, которые доступны только авторизованным пользователям. В-третьих, admin-маршруты, которые доступны только администратору магазина. Давайте все маршруты определим в компоненте components/AppRouter.js.

import { Routes, Route } from 'react-router-dom'
import Shop from '../pages/Shop.js'
import Login from '../pages/Login.js'
import Signup from '../pages/Signup.js'
import Delivery from '../pages/Delivery.js'
import Contacts from '../pages/Contacts.js'
import NotFound from '../pages/NotFound.js'
import User from '../pages/User.js'
import Admin from '../pages/Admin.js'

const publicRoutes = [
    {path: '/', Component: Shop},
    {path: '/login', Component: Login},
    {path: '/signup', Component: Signup},
    {path: '/delivery', Component: Delivery},
    {path: '/contacts', Component: Contacts},
    {path: '*', Component: NotFound},
]

const authRoutes = [
    {path: '/user', Component: User},
]

const adminRoutes = [
    {path: '/admin', Component: Admin},
]

const AppRouter = () => {
    const isAuth = true
    const isAdmin = true
    return (
        <Routes>
            {publicRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
            {isAuth && authRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
            {isAdmin && adminRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
        </Routes>
    )
}

export default AppRouter

Теперь компонент App.js будет просто импортировать AppRouter.js:

import { BrowserRouter } from 'react-router-dom'
import AppRouter from './components/AppRouter.js'

const App = () => {
    return (
        <BrowserRouter>
            <AppRouter />
        </BrowserRouter>
    )
}

export default App

Панель навигации

Создаем новый компонент NavBar.js в директории conponents:

import { Container, Navbar, Nav } from 'react-bootstrap'

const NavBar = () => {
    const isAuth = true
    const isAdmin = true
    return (
        <Navbar bg="dark" variant="dark">
            <Container>
                <Navbar.Brand href="/">Магазин</Navbar.Brand>
                <Nav className="mr-auto">
                    <Nav.Link href="/delivery">Доставка</Nav.Link>
                    <Nav.Link href="/contacts">Контакты</Nav.Link>
                    {isAuth ? (
                        <Nav.Link to="/user">Личный кабинет</Nav.Link>
                    ) : (
                        <>
                            <Nav.Link to="/login">Войти</NavLink>
                            <Nav.Link to="/signup">Регистрация</Nav.Link>
                        </>
                    )}
                    {isAdmin && (
                        <Nav.Link href="/admin">Панель управления</Nav.Link>
                    )}
                </Nav>
            </Container>
        </Navbar>
    );
};

export default NavBar

Тут возникает трудность — как подружить компоненты react-bootstrap с компонентами react-router-dom? Сейчас у нас обычные ссылки, а нам нужны ссылки из react-router-dom — то есть <Link to="…"> и <NavLink to="…">, которые не будут загружать с сервера страницы целиком, а только изменять состояние приложения.

Нужно заменить <Nav.Link href="…"> на <NavLink to="…"> и добавить еще className, который можно посмотреть в панели разработчика браузера. Так что в итоге получился такой код компонента:

import { Container, Navbar, Nav } from 'react-bootstrap'
import { NavLink } from 'react-router-dom'

const NavBar = () => {
    const isAuth = true
    const isAdmin = true
    return (
        <Navbar bg="dark" variant="dark">
            <Container>
                <NavLink to="/" className="navbar-brand">Магазин</NavLink>
                <Nav className="ml-auto">
                    <NavLink to="/delivery" className="nav-link">Доставка</NavLink>
                    <NavLink to="/contacts" className="nav-link">Контакты</NavLink>
                    {isAuth ? (
                        <NavLink to="/user" className="nav-link">Личный кабинет</NavLink>
                    ) : (
                        <>
                            <NavLink to="/login" className="nav-link">Войти</NavLink>
                            <NavLink to="/signup" className="nav-link">Регистрация</NavLink>
                        </>
                    )}
                    {isAdmin && (
                        <NavLink to="/admin" className="nav-link">Панель управления</NavLink>
                    )}
                </Nav>
            </Container>
        </Navbar>
    )
}

export default NavBar

Контекст приложения

Создаем новый компонент AppContext.js в директории conponents:

import React from 'react'

const AppContext = React.createContext()

// контекст, который будем передавать
const context = {
    user: {
        email: 'ivanov@mail.ru',
        isAuth: true,
        isAdmin: false,
    }
}

const AppContextProvider = (props) => {
    return (
        <AppContext.Provider value={context}>
            {props.children}
        </AppContext.Provider>
    );
}

export {AppContext, AppContextProvider}

Обернем все наше приложение в AppContextProvider, чтобы в любом месте иметь доступ к контексту через хук useContext.

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { AppContextProvider } from './components/AppContext.js';

ReactDOM.render(
    <React.StrictMode>
        <AppContextProvider>
            <App />
        </AppContextProvider>
    </React.StrictMode>,
    document.getElementById('root')
);

Теперь в компонентах AppRouter и NavBar вместо констант isAuth и isAdmin будем использовать данные из контекста.

/* .......... */
import { AppContext } from './AppContext.js'
import { useContext } from 'react'
/* .......... */
const AppRouter = () => {
    const { user } = useContext(AppContext)
    return (
        <Routes>
            {publicRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
            {user.isAuth && authRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
            {user.isAdmin && adminRoutes.map(({path, Component}) =>
                <Route key={path} path={path} element={<Component />} />
            )}
        </Routes>
    )
}

export default AppRouter
/* .......... */
import { AppContext } from './AppContext.js'
import { useContext } from 'react'

const NavBar = () => {
    const { user } = useContext(AppContext)
    return (
        <Navbar bg="dark" variant="dark">
            <Container>
                <NavLink to="/" className="navbar-brand">Магазин</NavLink>
                <Nav className="ml-auto">
                    <NavLink to="/delivery" className="nav-link">Доставка</Nav.Link>
                    <NavLink to="/contacts" className="nav-link">Контакты</Nav.Link>
                    {user.isAuth ? (.....) : (.....)}
                    {user.isAdmin && (.....)}
                </Nav>
            </Container>
        </Navbar>
    )
}

export default NavBar

Поиск: Backend • Express.js • Frontend • JavaScript • Node.js • ORM • React.js • Web-разработка • База данных • Интернет магазин • Каталог товаров • Корзина • Фреймворк

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.