React Router props.match и props.history.push не определены при использовании useContext - PullRequest
0 голосов
/ 06 ноября 2019

Я переместил свой проект в Codesandbox для лучшей помощи в моем вопросе. Вот ссылка на проект.

У меня есть два компонента SearchForm и AnimeDetails, которые получают вызовы API от моего компонента контекста AnimeContext. Форма предназначена для отображения запрошенного аниме, которое было запрошено, а AnimeDetails должно отображать детали для выбранного аниме.

Я использую props.match.params для получения идентификатора аниме в компоненте TopAnime, используя <Link to={} /> и props.history.push для перенаправления на новую страницу после отправки формы.

Когда я пытаюсь нажать на карту Аниме, чтобы получить подробности, я получаю

props.match не определен

Когда я отправляю формуЯ вижу появившееся искомое аниме, но затем получаю

props.history не определено

Я предполагаю, что это проблема React Router, и я ее не устанавливаючто-то правильно.

Вот что я пытался сделать до сих пор, но ничего не получалось:

  • Использование Redirect

  • Использование useHistory hook

  • Упаковка AnimeProvider с withRouter

Короче говоря, я не могу искать названия и не могу нажимать на анименазвание на главной странице, чтобы получить его детали, не получив неопределенных для props.match и props.history.

SearchForm компонент

import React, { useContext } from 'react';
import { withRouter } from 'react-router-dom'
import styled from 'styled-components'
import AnimeCard from './AnimeCard/AnimeCard';
import { AnimeContext } from '../store/AnimeContext'


const SearchForm = () => {
  const { dataItems, animeSearched, handleSubmit } = useContext(AnimeContext)

  return (
    <div>
      <Form onSubmit={handleSubmit}>
        <Input
          type="text"
          name="anime"
          placeholder="Enter title"
        // ref={value => myValue = value}
        />
        <FormButton type='submit'>Search</FormButton>
      </ Form>
      {animeSearched
        ?
        <AnimeCard />
        : null}
    </div>
  )
}

export default withRouter(SearchForm)

AnimeDetails компонент

import React, { useContext, useEffect } from "react";
import styled from "styled-components";

import { AnimeContext } from "../store/AnimeContext";

const AnimeDetails = () => {
  const { fetching, anime, fetchAnimeDetails } = useContext(AnimeContext);

  useEffect(() => {
    fetchAnimeDetails();
  });

  return (
    <>
      {fetching && "Fetching..."}
      {anime && (
        <AnimeDetailsWrapper>
          <AnimeDetailsContainer>
            <Poster src={anime.image_url} />
            {/* Details */}
            <Details>
              <Title>{anime.title}</Title>
              <TitleJpn>{anime.title_japanese}</TitleJpn>
              <Score>{anime.score || "N/A"}</Score>
              {/* If no score then display N/A */}
              <SongList>
                <h3>Opening Themes</h3>
                {anime.opening_themes // Make sure data is fully loaded before component renders
                  ? anime.opening_themes.map((song, index) => (
                      <li key={index}>{song}</li>
                    ))
                  : null}
              </SongList>
            </Details>
            {/* Info Bar */}
            <InfoBar>
              {
                <li>
                  Epiosdes: <span className="info-span">{anime.episodes}</span>
                </li>
              }
              {
                <li>
                  Duration: <span className="info-span">{anime.duration}</span>
                </li>
              }
              {
                <li>
                  <a
                    href={anime.trailer_url}
                    rel="external noopener noreferrer"
                    target="_blank"
                  >
                    View Trailer
                  </a>
                </li>
              }
            </InfoBar>
            {/* Synopsis */}
            <Synopsis>{anime.synopsis}</Synopsis>
          </AnimeDetailsContainer>
        </AnimeDetailsWrapper>
      )}
    </>
  );
};

export default AnimeDetails;

AnimeContext компонент

import React, { useState, useEffect, createContext } from 'react'

const AnimeContext = createContext()

const API = "https://api.jikan.moe/v3"


const AnimeProvider = (props) => {
  const urls = [
    `${API}/top/anime/1/airing`,
    `${API}/top/anime/1/tv`,
    `${API}/top/anime/1/upcoming`,
  ]

  // State for top Anime 
  const [topTv, setTopTv] = useState([])
  const [topAiring, setTopAiring] = useState([])
  const [topUpcoming, setTopUpcoming] = useState([])

  // State for Anime details
  const [animeReq, setAnimeReq] = useState({
    fetching: false,
    anime: []
  })

  // State for Anime search form
  const [dataItems, setDataItems] = useState([])
  const [animeSearched, setAnimeSearched] = useState(false)


  // Fetch top Anime 
  const fetchTopAnime = async () => {
    return Promise.all(
      urls.map(async url => {
        return await fetch(url); // fetch data from urls
      })
    )
      .then((responses) => Promise.all(responses.map(resp => resp.json())) // turn data into JSON
        .then(data => {
          const topTvFiltered = data[0].top.filter(item => item.rank <= 5) // filter out top 6 
          const topAiringFiltered = data[1].top.filter(item => item.rank <= 5)
          const topUpcomingFiltered = data[2].top.filter(item => item.rank <= 5)

          setTopTv(topTvFiltered)
          setTopAiring(topAiringFiltered)
          setTopUpcoming(topUpcomingFiltered)

          console.log(data)
        })
      )
      .catch(err => console.log("There was an error:" + err))
  }

  useEffect(() => {
    fetchTopAnime()
  }, [])

  // Fetch Anime details
  const fetchAnimeDetails = async () => {
    setAnimeReq({ fetching: true })

    const response = await fetch(`${API}/${props.match.params.animeId}`)
    const data = await response.json()

    console.log(data);
    setAnimeReq({ fetching: false, anime: data }) // set initial state to hold data from our API call
  }

  const { fetching, anime } = animeReq;

  // Fetch searched Anime
  async function handleSubmit(e) {
    e.preventDefault()

    const animeQuery = e.target.elements.anime.value
    const response = await fetch(`${API}/search/anime?q=${animeQuery}&page=1`)
    const animeData = await response.json()

    setDataItems(animeData.results)
    setAnimeSearched(!animeSearched)

    props.history.push('/dashboard')

  }


  return (
    <AnimeContext.Provider value={{
      topTv,
      setTopTv,
      topAiring,
      setTopAiring,
      topUpcoming,
      setTopUpcoming,
      dataItems,
      setDataItems,
      animeSearched,
      setAnimeSearched,
      fetching,
      anime,
      fetchTopAnime,
      fetchAnimeDetails,
      handleSubmit
    }}>
      {props.children}
    </AnimeContext.Provider>
  )
}

export { AnimeProvider, AnimeContext }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...