我正在使用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
});
}
}
为此组件拉入的数据只是一个具有单个object
的简单array
。
这是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 是一个异步操作 - 所以你的问题是你在从服务器获取数据之前添加 Row 组件。 推迟渲染<Row>
,直到您收集完相册。
return (
<section className="cp-new-releases">
{ albums && // you might also want to test length here
// - unless you want to show a message in your
// Row component, indicating that non were found.
<Row sectionHeading={`New Releases`} albums={albums}/>
}
</section>
);
现在,您的 Row 元素不会在初始加载时创建,而是等到您获得数据,然后添加它。 可以把它想象成,调用一个函数,你不会不必要地调用它,并且只使用有效数据。最终,这就是您在向渲染函数添加组件时所要执行的操作。因此,您必须添加条件逻辑,因为组件是有效的函数,应该根据特定用例的逻辑进行调用。