React Context 不渲染组件
React Context doesn't render component
我正在使用 context 和 React Router。当我创建我的上下文并包装我的组件时,没有呈现任何内容。当我检查控制台时,我可以看到我正在获取的记录数据(它来自 AnimeContext
中的 useEffect),但是 Header 和 HomePage 组件没有出现。
我正在尝试在 HomePage
上显示 topTv
、topAiring
和 topUpcoming
。
这是repo
上下文文件
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;
主页组件
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
如果我将 AnimeProvider
移到 Header
下方,我可以这样查看 header:
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>
);
所以我要么遗漏了一些重要的东西,要么我不理解 Context 是如何工作的and/or React Router。
AnimeContext.Consumer
需要一个 函数 作为 child,而且如果你使用 useContext
那么就不需要包装你的组件AnimeContext.Consumer
排在第一位。
此外,由于您的提供商传递了一个 object,那么 useContext
将 return 一个 object,因此您需要 object 解构而不是数组解构.
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>
}
}
您在 AnimeProvider
中有错字 - 它应该呈现 {props.children}
而不是 {props.childen}
我正在使用 context 和 React Router。当我创建我的上下文并包装我的组件时,没有呈现任何内容。当我检查控制台时,我可以看到我正在获取的记录数据(它来自 AnimeContext
中的 useEffect),但是 Header 和 HomePage 组件没有出现。
我正在尝试在 HomePage
上显示 topTv
、topAiring
和 topUpcoming
。
这是repo
上下文文件
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;
主页组件
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
如果我将 AnimeProvider
移到 Header
下方,我可以这样查看 header:
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>
);
所以我要么遗漏了一些重要的东西,要么我不理解 Context 是如何工作的and/or React Router。
AnimeContext.Consumer
需要一个 函数 作为 child,而且如果你使用 useContext
那么就不需要包装你的组件AnimeContext.Consumer
排在第一位。
此外,由于您的提供商传递了一个 object,那么 useContext
将 return 一个 object,因此您需要 object 解构而不是数组解构.
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>
}
}
您在 AnimeProvider
中有错字 - 它应该呈现 {props.children}
而不是 {props.childen}