【发布时间】:2020-03-03 08:22:25
【问题描述】:
我已将我的项目移至 Codesandbox,以便更好地解决我的问题。这是link to the project.
我有两个组件 SearchForm 和 AnimeDetails 接收来自我的上下文组件 AnimeContext 的 API 调用。该表单旨在显示所请求的搜索动漫,AnimeDetails 应该显示所选动漫的详细信息。
我正在使用props.match.params 获取TopAnime 组件中动画的ID,使用<Link to={} /> 和props.history.push 在提交表单后重定向到新页面。
当我尝试点击动漫卡片以获取详细信息时,我收到了
props.match 未定义
当我提交表单时,我看到搜索到的动漫出现了,但随后我收到了
props.history 未定义
我假设这是 React Router 问题,并且我没有正确设置某些东西。
这是我到目前为止所做的尝试,但没有任何效果:
使用
Redirect使用
useHistory挂钩用
withRouter包装AnimeProvider
简而言之,如果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 }
【问题讨论】:
-
这能回答你的问题吗? How to push to History in React Router v4?
-
它帮助我更好地理解了问题的一部分,但我仍然对
props.match有疑问,因为它仍未定义。我将它从我的上下文组件中删除,并直接在我的详细信息组件中使用它以使其现在可以正常工作。谢谢。
标签: reactjs react-router react-hooks react-router-v4 react-router-dom