До сих пор мы сосредоточились на внутренней логике нашего проекта, но, поскольку нашему API нужен клиент для отображения его данных, в этом разделе мы увидим, как подключить наш API-интерфейс flask к веб-приложению реагирования. React — это библиотека JavaScript, разработанная Facebook, которая использовалась для создания Instagram.com. React позволяет разработчикам легко создавать быстрые пользовательские интерфейсы для веб-сайтов и приложений.

Запускаем наше реагирующее приложение:

В этом разделе я не буду уделять слишком много внимания фронтенд-коду, так как эта серия руководств больше посвящена бэкенду, и я знаю, как быстро css может стать головной болью для многих бэкэнд-разработчиков, и я один из них 😄. Все это можно найти в github-репозитории проекта.

Хорошо, теперь, когда вы клонировали репозиторий на свой компьютер, перейдите в папку `client`, затем в файле `App.jsx` замените `‹Cart/ ›` с `‹Home/›`, ваш файл `App.jsx` должен выглядеть следующим образом:

Затем запустите свое реагирующее приложение с помощью команды `npm run dev`, если у вас возникнут какие-либо проблемы с этой командой, я приглашаю вас снова прочитать README, который находится в репозитории github проекта. Не беспокойтесь, если у вас есть ошибки в консоли, мы исправим их в этом руководстве. Затем перейдите в папку `flask_api`, где находится наш API, и запустите сервер flask с помощью команды `flask run`.

Теперь вернитесь в папку клиента и перейдите в папку `src`, затем перейдите в папку `components`, затем в папку `PopularBooks`. папку и, наконец, откройте файл `PopularBooks.jsx`. Я знаю, что папок много, но именно так я организую свои реактивные проекты с каждым компонентом в соответствующей папке. Вы должны иметь этот код структуры:

Затем импортируйте хуки `useEffect` и `useState` из реакции:

`useState` и `useEffect` — очень важные крючки реакции. Хуки обеспечивают локальное состояние и другие функции React без необходимости написания класса компонента. Это функции, которые позволяют подключаться к локальному состоянию и управлению жизненным циклом компонента React из функций.

Крючок useState

useState — это хук, который позволяет нам хранить данные, отправленные нашим API. useState() — это хук, который позволяет нам иметь переменные состояния в функциональных компонентах. Таким образом, useState позволяет инкапсулировать локальное состояние в функциональный компонент. Как правило, рекомендуется хранить данные нашего API в состоянии.

Хук useState — это специальная функция, которая принимает начальное состояние в качестве аргумента и возвращает массив из двух записей, которые я получаю в переменных `data` и `setData`. первая переменная `data` — это переменная состояния, начальное значение которой — это значение, переданное функции `useState`, в данном случае это пустой массив, а Переменная `setData` будет содержать метод: `setState`, который ставит в очередь все обновления, сделанные для состояния компонента, а затем просит React отобразить компонент и его дочерние элементы с обновленным состояние, для простоты это функция, которая позволит мне обновить мою переменную состояния.

Затем я использую хук `useEffect`, чтобы сделать запрос к моему API.

Крючок useEffect

`useEffect` – это хук, который позволяет асинхронно запускать функцию при изменении состояния компонента. Это можно использовать для применения побочных эффектов, таких как извлечение данных, прямое обновление DOM и таймеры.
useEffect принимает в качестве первого параметра функцию обратного вызова, которая будет выполняться при изменении одной из ее зависимостей (эта функция выполняется асинхронно и не будет блокировать отрисовку компонента). Второй параметр — это массив, который позволяет определить зависимости этого хука (это позволяет выполнять функцию только в том случае, если для этого компонента изменился элемент). Если вы не ставите никакой зависимости (пустой массив, как указано выше), в этом случае функция, переданная в первом параметре, будет выполняться только при монтировании компонента.

useEffect обычно используется для извлечения данных в React. Извлечение данных предполагает использование асинхронных функций. Чтобы сделать асинхронный вызов внутри хука useEffect, мы можем определить асинхронную функцию внутри useEffect. Вы можете задаться вопросом, почему мы определяем асинхронную функцию вместо прямого использования ключевого слова `async` в функции обратного вызова для `useEffect()`, например:

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

Затем все, что мне нужно сделать, это обновить переменную состояния данными, возвращаемыми моим API, но сначала, поскольку я хотел бы отобразить только первые 4 книги, я использую метод `slice()` для data.items, который вернет массив из 4 элементов, затем я перебираю его с помощью метода `map()` и, наконец, отправляю `book` в свой `BookCard` компонент.

import {useState,useEffect} from "react";
import BookCard from "../BookCard/BookCard";
import FindBook from "../FindBook/FindBook";
import axios, { isAxiosError } from "axios";
import './popular_book.css'

function PopuplarBooks() {
  const [data,setData] = useState([])

  useEffect(()=>{
    const fetchPosts = async () =>{
      const res = await axios.get('http://localhost:5000/api/books/')
      setData(res.data)
  }

  fetchPosts()
  },[])

  return (
    <>
      <FindBook/>
      <div className="popular-container" id="popular">
        <h1>Popular Books</h1>
        <div className="bookcard-container">
        {
          data.items? data.items.slice(0,4).map(book=>(
            <BookCard key={book.id} book={book} />
          )) : 'Loading'
      }
        </div>
      </div>
    </>
    
  )
}

export default PopuplarBooks

Чтобы закончить, перейдите в папку `BookCard`, а затем в файл `BookCard.jsx` и замените существующий код следующим:

import React from 'react'
import BookAuthor from '../BookAuthor/BookAuthor'
import TableBook from '../TableBook/TableBook'
import './bookInfo.css'
function BookInfo({book}) {
  return (
    <div className='bookinfo-wrapper'>
        <div className="content-wrapper">
            <div className="content-book-container">
                <div className="content-book">
                    <div className="book-imageContainer">
                        <img className='book-image' src={book?.book_url} alt="" />
                    </div>
                    <div className="book-summary">
                        <h1 className="product-title">{book?.title}</h1>
                        <div className="book-description">
                            <p>
                                {book?.description}
                            </p>
                        </div>
                    <div className="product-price">
                        <p className="price">${book?.price}</p>
                        <span className='in-stock'>289 in stock</span>
                        <form action="" className='cart'>
                            <div className="quantity">
                                <input id='quantity'  value={1} type="number" step={1} min={1} max={289} />
                                <button className='add-cart'>Add to cart</button>
                            </div>
                        </form>
                    </div>
                 </div>
                </div>
                <div className="product-details">
                    <div className="book-details-container">
                        <div className="col-first">
                            <div className="book-details">
                                <h3>Book Details</h3>
                                <TableBook book={book}/>
                            </div>
                        </div>
                        <div className="col-second">
                            <div className="book-author">
                                <BookAuthor authors={book?.authors[0]}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
  )
}

export default BookInfo

Повторите метод, описанный в разделе `PopularBooks`, в компоненте `NewBook` и отреагируйте на страницу. Если у вас возникли проблемы, вы можете скачать код этой главы прямо с github.

маршрутизация и реакция js

Маршрутизация — это процесс перенаправления пользователя на разные страницы в зависимости от его действия или запроса. В отличие от интерфейсных фреймворков, таких как Angular React, нет собственной системы маршрутизации, для реализации системы маршрутизации в нашем приложении React нам придется использовать внешнюю библиотеку React Router.

Как установить реагирующий роутер

Чтобы установить React Router в наш проект, нам просто нужно открыть наш терминал и ввести команду:

npm install react-router-domn

После успешной установки пакета теперь мы можем установить и настроить react-router в нашем проекте.

как настроить React Router

Чтобы настроить React Router, перейдите к файлу `main.jsx`, который является корневым, и импортируйте `BrowserRouter` из `react-router- dom`, который мы установили, обернув его вокруг нашего компонента `App` следующим образом:

Как настроить маршруты в React:

Теперь, когда мы успешно установили и импортировали React Router в наш проект, следующим шагом будет использование React Router для реализации маршрутизации. Первый шаг — настроить все наши маршруты (то есть все страницы/компоненты, к которым мы хотим перейти). наш сайт.
Теперь мы можем установить и настроить наши маршруты в файле `App.jsx`, который является основой нашего приложения React:

import { Route,Routes } from 'react-router'
import './App.css'
import Cart from './pages/Cart/Cart'
import Explore from './pages/Explore/Explore'
import Home from './pages/Home/Home'
import Single from './pages/single/Single'

function App() {

  return (
    <div className='app'>
      <Routes>
        <Route path='/' element={<Home/>}/>
        <Route path='/book/:book_id' element={<Single/>}/>
        <Route path='/explore' element={<Explore/>}/>
        <Route path='/cart' element={<Cart/>}/>
      </Routes>
    </div>
  )
}

export default App

из приведенного выше кода видно, что мы импортировали компоненты `Routes` и `Route` из react-router-dom, а затем использовали их для объявления маршрутов, которые мы хотеть. Все маршруты заключены в тег Routes, и эти маршруты имеют два основных свойства:

. `путь`: как следует из названия, он определяет путь, который мы хотим, чтобы пользователи выбрали, чтобы добраться до определенного компонента. Когда мы устанавливаем путь к /explore, например, когда пользователь добавляет /explore к URL-ссылке, он переходит на эту страницу.

. `element`: содержит компонент, который мы хотим загрузить в путь. Это просто понять, но не забудьте импортировать все компоненты, которые мы здесь используем, иначе произойдет ошибка.

Когда мы заходим в наш браузер и пытаемся перейти по URL-адресу, он загружает все содержимое этих страниц.

Как получить доступ к маршрутам по ссылкам:

Мы только что видели, как мы можем вручную получить доступ к этим маршрутам через URL-адрес, но это не должно иметь место для наших пользователей. Теперь давайте посмотрим, как мы можем создавать ссылки в нашем приложении, чтобы пользователи могли легко перемещаться по нему.

Давайте добавим ссылку на наши разные книжные карточки, чтобы пользователи могли получить доступ к информации о конкретной книге:

мы сначала импортировали `Link` из react-router-dom, а затем добавили свойство `to` на основе пути, который мы указали при настройке наших маршрутов. Обратите внимание, что мы использовали интерполяцию строк для динамического присоединения идентификатора каждой книги к пути, чтобы мы могли получить этот идентификатор и использовать его для отображения данных.

Как использовать параметры URL для управления динамической маршрутизацией:

Мы импортируем хук `useParams` в компонент `Single.jsx`, чтобы мы могли использовать его для получения параметра URL, а затем использовать этот параметр для запроса нашего API. .

После импорта `useParams` я воспользуюсь хуком useParams, а затем деструктурирую его, чтобы получить прямой доступ к параметру `book_id` и использовать его в нашем приложении:

На данный момент мы успешно передали параметр URL, теперь давайте используем этот идентификатор для получения данных из нашего API:

import {useState,useEffect} from 'react'
import Navbar from '../../components/Navbar/Navbar'
import FindBook from '../../components/FindBook/FindBook'
import './single.css'
import BookInfo from '../../components/BookInfo/BookInfo'
import Footer from '../../components/Footer/Footer'
import { useParams } from 'react-router'
import axios from 'axios'
import { Link } from 'react-router-dom'

function Single() {
  const {book_id} = useParams()
  const [book,setBook] = useState()

  useEffect(()=>{
      const fectchBook = async()=>{
        const res = await axios.get(`http://localhost:5000/api/books/${book_id}`)
        setBook(res.data)
      }
      fectchBook()
  },[book_id])

  console.log(book)
  return (
    <div className='single-container'>
        <Navbar color={true}/>
        <div className="single-wrapper">
            <section className='page-title'>
                <h2>Bookstore</h2>
                <Link to={'/'}>Home /</Link> {book?.title}
            </section>
            <FindBook/>
            <BookInfo book={book}/>
            <Footer/>
        </div>
    </div>
  )
}

export default Single

Наконец, давайте перейдем к папке `BookInfo`, чтобы получить информацию о книге из родительского элемента `Single`:

import React from 'react'
import BookAuthor from '../BookAuthor/BookAuthor'
import TableBook from '../TableBook/TableBook'
import './bookInfo.css'
function BookInfo({book}) {
  return (
    <div className='bookinfo-wrapper'>
        <div className="content-wrapper">
            <div className="content-book-container">
                <div className="content-book">
                    <div className="book-imageContainer">
                        <img className='book-image' src={book?.book_url} alt="" />
                    </div>
                    <div className="book-summary">
                        <h1 className="product-title">{book?.title}</h1>
                        <div className="book-description">
                            <p>
                                {book?.description}
                            </p>
                        </div>
                    <div className="product-price">
                        <p className="price">${book?.price}</p>
                        <span className='in-stock'>289 in stock</span>
                        <form action="" className='cart'>
                            <div className="quantity">
                                <input id='quantity'  value={1} type="number" step={1} min={1} max={289} />
                                <button className='add-cart'>Add to cart</button>
                            </div>
                        </form>
                    </div>
                 </div>
                </div>
                <div className="product-details">
                    <div className="book-details-container">
                        <div className="col-first">
                            <div className="book-details">
                                <h3>Book Details</h3>
                                <TableBook book={book}/>
                            </div>
                        </div>
                        <div className="col-second">
                            <div className="book-author">
                                <BookAuthor authors={book?.authors[0]}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
  )
}

export default BookInfo

Как устранить ошибку «Маршруты не найдены» в маршрутизаторе React?

При маршрутизации может случиться так, что пользователь обращается к ненастроенному маршруту или маршруту, который не существует; когда это происходит, React ничего не отображает на экране.

Эту проблему можно решить, настроив новый маршрут для возврата определенного компонента, когда пользователь переходит к ненастроенному маршруту, следующим образом:

В приведенном выше коде мы создали маршрут с путем `*`, чтобы получить все ненастроенные пути и назначить их компоненту `NotFound`.

Примечание. Мы создали компонент под названием NotFound.jsx, но вы можете назвать его как угодно, чтобы отображалось сообщение об ошибке 404 на экране, чтобы пользователи знали, что они находятся на неправильной странице. Мы также можем добавить кнопку, которая переводит пользователя на другую страницу или обратно.

Вот ссылка на репозиторий github, не стесняйтесь загружать код напрямую, если у вас есть проблемы. Это было все для меня, и мы увидимся на следующей неделе, когда мы реализуем функцию поиска в нашем API с использованием распределенной поисковой системы, такой как elasticsearch.