React Router v6 — доступ к вложенным маршрутам и правильный способ обработки URL-адреса, написанного от руки

Я создаю библиотечный сервис, используя в основном react, redux-toolkit и react-router.

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

  1. Самое важное. Есть кое-что, по чему я скучаю. Я пробовал следующее, но не работает. Я попытался navigate перейти к /catalogue/:bookId с главной страницы и страницы каталога, но не отображается правильный компонент. Я думаю, что неправильно понял смысл детей внутри объекта маршрута, есть предложения? Должен ли я использовать <Outlet /> и какой поток для этого правильный?

Часть router.tsx

      {
        path: '/',
        element: <Home />,
      },
      {
        path: 'catalogue',
        element: <Catalogue />,
        children: [
          {
            path: '/catalogue/:bookId',
            element: <BookDetails />
          }
        ]
      },

Часть компонента <Catalogue />, в котором я визуализирую для каждой книги в списке.

       {
          list.map((book, i) => {
            return (
              <BookCard
                book = {book}
                key = {`book-${i}`} />
            )
          })
        }

<BookCard /> компонент

const BookCard = ({ book }: {
  book: IBook;
}) => {
  const dispatch = useAppDispatch();
  const navigate = useNavigate();

  const handleClick = () => {
    dispatch(exploredBook(book));
    navigate(`/catalogue/${book.id}`)
  }

  return (
    <Col>
      <Card className = "book-card">
        <Row>
          <Col>
            <Card.Img 
              className = "book-img"
              variant = "top"
              src = {book.imageLink}
              alt = {`${book.title} book cover`} />            
          </Col>
          <Col>
            <Card.Body>
              <Card.Title as = {Link} to = {`/catalogue/${book.id}`}>{book.title}</Card.Title>
              <Card.Subtitle>{book.author}, {book.year}</Card.Subtitle>
              {  
                book.book_status.copies <= 0
                  ? <Badge bg = "danger">Out of stock</Badge>
                  : <Badge bg = "success">{book.book_status.copies} copies available</Badge>
              }
              <CatalogueActions book = {book} />
              <Card.Link href = {book.link} target='_blank'>
                <small>More about <em>{book.title}</em> on Wikipedia</small>
              </Card.Link>
            </Card.Body>
          </Col>
        </Row>
      </Card>
    </Col>
  )
};

export default BookCard;
  1. Мой второй вопрос: когда пользователь переходит к деталям книги, URL-адрес должен быть чем-то вроде /localhost:3000/catalogue/o23ifo2eifj8249g2g24g. Но если пользователь напишет вручную URL-адрес и вставит случайный идентификатор, страница все равно отобразит содержимое. Как я могу предотвратить рендеринг контента? Моим простым решением было бы выполнить поиск в database, используя хук useParams() для письменного идентификатора: если существует, отображается соответствующая книга, если не существует, отображается страница 404. Есть ли лучшее более простое решение?

🤔 А знаете ли вы, что...
React поддерживает создание контролируемых и неконтролируемых компонентов форм.


3
59
1

Ответ:

Решено

С текущей конфигурацией маршрутов:

{
  path: '/',
  element: <Home />,
},
{
  path: 'catalogue',
  element: <Catalogue />,
  children: [
    {
      path: '/catalogue/:bookId', // <-- or simply ":bookId"
      element: <BookDetails />
    }
  ]
},

Отрисовывается вложенный маршрут на path = "/catalogue/:bookId".

Должен ли я использовать <Outlet /> и какой поток для этого правильный?

Да, в этой конфигурации компонент Catalogue обязательно должен отображать компонент Outlet для вложенных маршрутов, чтобы отображать их содержимое. Это случай использования, когда вы хотите отображать Catalogue и BookDetails одновременно.

Пример каталога:

{list.map((book, i) => {
  return (
    <BookCard
      book = {book}
      key = {`book-${i}`} />
    )
  })
}
...

<Outlet />

Если это не так, и вы хотите, чтобы Catalogue и BookDetails отображались каждый на своих независимых страницах, то маршруты должны быть невложенными.

Пример:

{
  path: '/',
  element: <Home />,
},
{
  path: 'catalogue',
  element: <Catalogue />,
},
{
  path: '/catalogue/:bookId',
  element: <BookDetails />
}

или

{
  path: '/',
  element: <Home />,
},
{
  path: 'catalogue',
  children: [
    {
      index: true,
      element: <Catalogue />
    },
    {
      path: ':bookId',
      element: <BookDetails />
    }
  ]
},

Когда пользователь доходит до деталей книга, URL-адрес должен быть примерно таким "/catalogue/o23ifo2eifj8249g2g24g". Если пользователь, напишите вручную URL-адрес и вставьте случайный идентификатор, страница будет отображаться тем не менее содержание. Как я могу предотвратить рендеринг содержание? Моим простым решением было бы сделать поиск в базе данных используя хук useParams() для письменного идентификатора: если существует, отображается соответствующая книга, если она не существует, отображается страница 404 предоставлено. Есть ли лучшее более простое решение?

Это стандартная практика для проверки параметров пути динамического маршрута и либо перенаправления пользователя на страницу 404, либо условного отображения некоторого резервного пользовательского интерфейса на странице BookDetails при использовании недопустимого значения bookId.