七. React的上下文(Context)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了七. React的上下文(Context)相关的知识,希望对你有一定的参考价值。
参考技术A 在某些场景下,想在整个组件数中传递数据,但却不想手动地在每一层传递属性。可以直接在React中使用强大的“context”API解决上述问题。如果希望使用应用程序更加稳定,就不要使用上下文(context)。这只是一个实验性的API,并且可能在未来的React版本中移除。
相比于Context,更提倡使用Redux或者使用props和state。
如果 contextTypes 在组件中定义,下列的 生命周期方法 将接受一个额外的参数, context 对象:
React Context 不渲染组件
【中文标题】React Context 不渲染组件【英文标题】:React Context doesn't render component 【发布时间】:2020-02-29 00:14:14 【问题描述】:我正在使用上下文和 React 路由器。当我创建上下文并包装我的组件时,没有渲染任何内容。当我检查控制台时,我可以看到我正在获取的记录数据(它来自 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
下方,我可以像这样查看标题:
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 的工作原理和/或 React Router。
【问题讨论】:
【参考方案1】:您在 AnimeProvider
中有错字 - 它应该呈现 props.children
而不是 props.childen
【讨论】:
我不敢相信我犯了这样一个微不足道的错误。非常感谢您的时间和帮助。【参考方案2】:AnimeContext.Consumer
需要一个 function 作为子级,如果您使用的是useContext
,则无需首先使用AnimeContext.Consumer
包装您的组件。
此外,由于您的提供者传递了一个对象,那么useContext
将返回一个对象,因此您需要对象解构而不是数组解构。
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>
【讨论】:
我进行了更改,但我的HomePage
仍然是空白的。什么都没有渲染。
另外,你有没有从Switch
内部移动AnimeProvider
?
当我将它作为父级移到顶部时,没有任何渲染。当它位于Switch
正下方时,唯一呈现的是Header
。
这里是我的回购链接Repo以上是关于七. React的上下文(Context)的主要内容,如果未能解决你的问题,请参考以下文章
React/React Context API:使用 useEffect() 钩子时等待上下文值
React Context API,从子组件设置上下文状态,而不是将函数作为道具传递