javascript - 为什么我的 React 组件渲染两次?
问题描述
我正在使用React-hooks
和Context API
状态管理。当组件呈现时,我已经控制台注销了context
状态。consumer
在console
我注意到状态从context
被注销两次。这是为什么?
我正在使用从本地服务器fetch API
中提取数据。node
因此,在前几行中,console
我得到了context
eg的初始状态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
变量<Row/>
作为prop
.
行组件:
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
变量是一个array
包含两个objects
。如果我尝试遍历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 元素不是在初始加载时创建的,而是等到你得到数据,然后添加它。想象一下,调用一个函数,你不会不必要地调用它,并且只使用有效数据。最终,这就是您将组件添加到渲染函数时所做的事情。所以你必须添加条件逻辑,因为组件是有效的函数,应该根据你的特定用例的逻辑来调用。
推荐阅读
- java - 如何使用 IAM 角色从我的本地计算机访问 S3
- kubernetes - 如何拥有多个 Kubernetes 服务对象本身的实例?
- jquery - 在gridview上应用jquery数据表和引导程序后,为什么它的列相距很远?
- node.js - 无法将 Stream 对象发送到 NodeJS 服务器
- git - 如何在 mergetool 期间选择整个 REMOTE 文件?
- excel - 使用 FSO 使用扩展通配符在记事本中打开 txt 文件
- laravel - 使用 Laravel 中的另一个关联字段为 dateRange 创建条件
- javascript - 覆盖本地存储
- ios - 我想要设计一堆用曲线连接的圆圈
- c# - 返回浮点数组时出现错误