【发布时间】:2020-09-07 20:20:19
【问题描述】:
我使用React-hooks 和Context API 进行状态管理。当consumer 组件呈现时,我已经控制台注销了context 状态。在console 中,我注意到context 的状态被注销了两次。这是为什么?
我正在使用fetch API 从我的本地node 服务器中提取数据。所以,在console 的前几行中,我得到了context 的初始状态,例如undefined、null——然后就在它下面,我得到了从服务器拉入数据的更新状态。
我创建了一个“大师”context 来分享functions,我将在整个应用程序中使用它。 fetch 函数使用的是async/await:
import React, { Component, createContext } from 'react';
export const MasterContext = createContext();
export class MasterProvider extends Component {
state = {
fetchData: async (url) => {
let res = await fetch(url);
let data = await res.json();
return data;
}
}
render() {
return (
<MasterContext.Provider value={{...this.state}}>
{this.props.children}
</MasterContext.Provider>
);
}
}
我有两个组件,使用两个不同的 contexts - 一个 context 非常简单
state = {
title: '',
body: '',
cta: {
text: ''
},
img: '',
setHeaderPromo: (res) => {
this.setState({
title: res[0].title,
body: res[0].body,
cta: {...this.state.cta, ...res[0].cta},
img: res[0].img
});
}
}
为此组件提取的数据只是一个简单的array 和一个object。
这是consumer 组件:
const HeaderPromo = () => {
const {title, body, cta, img, setHeaderPromo} = useContext(HeaderContext);
const {fetchData} = useContext(MasterContext);
useEffect(() => {
fetchData(`http://localhost:5000/api/header`)
.then((res) => {
setHeaderPromo(res);
});
}, [fetchData, setHeaderPromo]);
console.log(title);
return (
<article className="cp-header__promo">
<div className="cp-header__promo__image">
<img src={img} alt="promo"/>
</div>
<div className="cp-header__promo__copy">
<h3>{title}</h3>
<p>{body}</p>
<button>{cta.text}</button>
</div>
</article>
);
}
所以,这在技术上是可行的。但是,我注意到当我注销 title 变量时,它会输出两次。第一次是context的初始状态,第二次输出数据的内容。为什么要这样做?
我的问题在于我的第二个组件 - context 状态只是一个空的 array,在 fetch 请求之后被填充。
第二个context:
state = {
albums: null,
setNewReleases: (res) => {
this.setState({
albums: res
});
}
}
这是两个consumer 组件:
const NewReleases = () => {
const {albums, setNewReleases} = useContext(NewReleaseContext);
const {fetchData} = useContext(MasterContext);
useEffect(() => {
fetchData(`http://localhost:5000/api/new-releases`)
.then((res) => {
setNewReleases(res);
});
}, [fetchData, setNewReleases]);
console.log('from newRelease component', albums);
return (
<section className="cp-new-releases">
<Row sectionHeading={`New Releases`} albums={albums}/>
</section>
);
}
export default NewReleases;
因此,albums 变量再次被注销两次。首先,初始状态,然后数据被拉入,并再次记录。
现在,我将这个albums 变量作为prop 传递给<Row/> 组件。
行组件:
const Row = ({sectionHeading, albums}) => {
console.log('from row component', albums);
return (
<Fragment>
<div className="cp-new-releases__row">
{(sectionHeading) && (<h3 className="row-title">{sectionHeading}</h3>)}
<div className="cp-new-releases__row__album-container">
{albums.map((item, index) => (
<Album img={item.img} title={item.title} key={index}/>
))}
</div>
</div>
</Fragment>
);
}
export default Row;
现在albums 变量是一个包含两个objects 的array。如果我尝试遍历array,它会抛出错误Uncaught TypeError: Cannot read property 'map' of null。
我可以解决这个问题的唯一方法是通过if 检查albums 是否有一个值。
const Row = ({sectionHeading, albums}) => {
console.log('from row component', albums);
return (
<Fragment>
<div className="cp-new-releases__row">
{(sectionHeading) && (<h3 className="row-title">{sectionHeading}</h3>)}
<div className="cp-new-releases__row__album-container">
{(albums) ? (
albums.map((item, index) => (
<Album img={item.img} title={item.title} key={index}/>
))
) : (<p style={{color: 'red'}}>no album</p>)}
</div>
</div>
</Fragment>
);
}
export default Row;
它试图循环遍历context 的初始状态。有人可以解释发生了什么,以及如何改进我的代码吗?
我知道这是啰嗦。所以,如果你坚持到了最后,你就是冠军。
谢谢
【问题讨论】:
-
fetchData 是异步的,所以你有你的初始渲染,然后当异步完成更新状态时另一个。您可以检查相册是否为空,并且不渲染任何子组件,然后当您的 fetch 返回并设置相册时,渲染器可以添加您的组件
标签: javascript reactjs react-hooks react-context