I'm using context and React Router. When I created my context and wrapped my components, nothing rendered. When I check the console, I can see the logged data that I'm fetching (it's coming from useEffect in AnimeContext
) but the Header and HomePage components don't appear.
I'm attempting to display topTv
, topAiring
, and topUpcoming
on the HomePage
.
Here's the repo
Context file
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
}
// Fetch searched Anime
const handleSubmit = async (e) => {
e.preventDefault()
const animeQuery = e.target.elements.anime.value
const response = await fetch(`${API}/search/anime?q=${animeQuery}&page=1`)
// const response2 = await fetch(`${API}/top/anime/1/movie`)
const animeData = await response.json()
// const topAnime = await response2.json()
setDataItems(animeData.results)
setAnimeSearched(!animeSearched)
props.history.push('dashboard')
}
const { fetching, anime } = animeReq;
return (
<AnimeContext.Provider value={{
topTv,
setTopTv,
topAiring,
setTopAiring,
topUpcoming,
setTopUpcoming,
dataItems,
setDataItems,
animeSearched,
setAnimeSearched,
fetching,
anime,
fetchTopAnime,
fetchAnimeDetails,
handleSubmit
}}>
{props.childen}
</AnimeContext.Provider>
)
}
export { AnimeProvider, AnimeContext }
App.js
import React, { Component } from 'react';
import styled, { ThemeProvider } from 'styled-components';
import theme from './config/theme';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { AnimeProvider } from './store/AnimeContext'
import Header from './Components/Header';
import HomePage from './Components/Home Page/HomePage';
import AnimeDetails from './Components/AnimeDetails';
import AnimeCard from './Components/AnimeCard/AnimeCard'
class App extends Component {
render() {
return (
<AnimeProvider>
<Router>
<ThemeProvider theme={theme}>
<AppWrapper>
<Header />
<Switch>
<Route path='/' exact component={HomePage} />
<Route path='/dashboard' exact component={AnimeCard} />
<Route path='/:animeId' component={AnimeDetails} />
</Switch>
</AppWrapper>
</ThemeProvider>
</Router>
</AnimeProvider>
);
}
}
const AppWrapper = styled.div`
text-align: center;
font-size: calc(10px + 1vmin);
color: white;
`
export default App;
HomePage Component
import React, { useContext } from 'react'
import styled from 'styled-components'
import { TopAnime } from './TopAnime';
import { AnimeContext } from '../../store/AnimeContext'
const HomePage = () => {
const [topTv, topAiring, topUpcoming,] = useContext(AnimeContext)
return (
<AnimeContext.Consumer>
<HomeWrapper>
<TopAni>
{topTv.length > 0 ? <TopAniTitle>Top TV</TopAniTitle> : null}
{topTv.map((item, index) => (
<TopAnime
key={index}
image={item.image_url}
title={item.title}
item={item}
/>
))}
</TopAni>
<TopAni>
{topAiring.length > 0 ? <TopAniTitle>Top Airing</TopAniTitle> : null}
{topAiring.map((item, index) => (
<TopAnime
key={index}
image={item.image_url}
title={item.title}
item={item}
/>
))}
</TopAni>
<TopAni>
{topUpcoming.length > 0 ? <TopAniTitle>Top Upcoming</TopAniTitle> : null}
{topUpcoming.map((item, index) => (
<TopAnime
key={index}
image={item.image_url}
title={item.title}
item={item}
/>
))}
</TopAni>
</HomeWrapper>
</AnimeContext.Consumer>
);
}
const HomeWrapper = styled.div`
height: 100%;
padding: 6rem 4.5rem;
color: ${props => props.theme.colors.white};
`
const TopAni = styled.div`
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-template-rows: auto;
padding: 1rem;
`
const TopAniTitle = styled.h2`
grid-column: 1 / -1;
justify-self: start;
`
export default HomePage
If I move my AnimeProvider
below the Header
, I am able to view the header like so:
return (
<Router>
<ThemeProvider theme={theme}>
<AppWrapper>
<Header />
<Switch>
<AnimeProvider>
<Route path='/' exact component={HomePage} />
<Route path='/dashboard' exact component={AnimeCard} />
<Route path='/:animeId' component={AnimeDetails} />
</AnimeProvider>
</Switch>
</AppWrapper>
</ThemeProvider>
</Router>
);
So I'm either missing something crucial or I'm not understanding how Context works and/or React Router.
You have a typo in AnimeProvider
- it should render {props.children}
not {props.childen}
AnimeContext.Consumer
requires a function as a child, also if you are using useContext
then there is no need to wrap your component with AnimeContext.Consumer
in first place.
Also since your provider pass down an object, then useContext
would return an object, so you need object destructuring instead of array destructuring.
const HomePage = () => {
const { topTv, topAiring, topUpcoming } = useContext(AnimeContext)
return (
<HomeWrapper>
<TopAni>
{topTv.length > 0 ? <TopAniTitle>Top TV</TopAniTitle> : null}
{topTv.map((item, index) => (
<TopAnime
key={index}
image={item.image_url}
title={item.title}
item={item}
/>
))}
</TopAni>
...
</HomeWrapper>
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.