[英]react component renders twice with only one setState in componentWillMount
我有這個組件,該角色的作用是從服務器獲取帶有fetch的類別對象,並將其放入componentWillMount中。 只有一個setState:當數據從服務器到達時,它位於componentWillMount承諾回調中。
問題在於,該組件第一次呈現兩次,this.state.category值是構造函數的初始狀態,然后第二次this.state.category等於從服務器接收到的對象。 在setState之后,如何使render方法僅調用一次?
import ReactDom from 'react-dom';
import React from 'react';
import Breadcrumb from './Breadcrumb';
export default class Category extends React.Component{
constructor(props){
super(props);
this.state = {
category: {
'published_projects':[{'images':[]}],
'unpublished_projects':[{'images':[]}],
}
};
}
componentWillMount(){
fetch(route('api.categories.show', {'slug':this.props.match.params.slug}))
.then(response => response.json())
.then(data => {this.setState({category: data});});
}
totalLikesCount(){
return this.state.category.published_projects.reduce(( accu, item ) =>
accu + item.likes_count ,0
);
}
totalViewsCount(){
return this.state.category.published_projects.reduce(( accu, item ) =>
accu + item.views_count ,0
);
}
totalPicturesCount(){
return this.state.category.published_projects.reduce(( accu, item ) =>
accu + item.images.length ,0
);
}
render(){
const category = this.state.category;
console.log(category);
return (
<div className="w980px center">
<div className="CategoryHeader-bg-wrapper margin-rt10 margin-lt10">
<img alt="" src={category.picture_url}/>
<div className="CategoryHeader-bg-gradient">
<h1>
{ category.name }
</h1>
</div>
</div>
<div className="CategoryHeader-metrics-wrapper u-boxShadow margin-rt10 margin-lt10">
<ul className="CategoryHeader-metrics-list">
<li className="CategoryHeader-metrics-item txt-center">
<span className="CategoryHeader-metrics-label">
Projets
</span>
<span className="CategoryHeader-metrics-value">
{ category.published_projects.length }
</span>
</li>
<li className="CategoryHeader-metrics-item txt-center">
<span className="CategoryHeader-metrics-label">
Total de mentions j'aime
</span>
<span className="CategoryHeader-metrics-value">
{ this.totalLikesCount() }
</span>
</li>
<li className="CategoryHeader-metrics-item txt-center">
<span className="CategoryHeader-metrics-label">
Total des vues
</span>
<span className="CategoryHeader-metrics-value">
{ this.totalViewsCount() }
</span>
</li>
<li className="CategoryHeader-metrics-item txt-center">
<span className="CategoryHeader-metrics-label">
Total des photos
</span>
<span className="CategoryHeader-metrics-value">
{ this.totalPicturesCount() }
</span>
</li>
</ul>
</div>
</div>
);
}
}
您的代碼可能要求兩次間接渲染:
// This calls to setState (but after a while)
fetch(route('api.categories.show', {'slug':this.props.match.params.slug}))
.then(response => response.json()) // These are not called yet
.then(data => {this.setState({category: data});});
// Then immediately after calling fetch, it gets to the next stages of the react lifecycles,
// (like render)
render() ...
// Then setState gets called because it is an asynchronous call,
.then(data => {this.setState({category: data});});
// and setState calls to render again.
問題是該組件渲染兩次
您有一個丑陋的,不建議使用的選項,該選項在首次render
(沒有獲取數據時)從render
return null
,並在有數據時正常返回,從而阻止了渲染該元素。
您還有另一個選擇是捕獲是否在其父級中獲取數據,然后將元素隱藏在那里。
基於React生命周期, componentWillMount
函數在渲染之前被調用...但這並不意味着渲染函數將等待componentWillMount
內部的async
調用開始渲染,因此您的組件將在獲取完成之前渲染一次,在獲取之后渲染一次已經完成了。
如果要確保僅在數據可用時才可以渲染組件,則可以在父組件中獲取數據,然后僅在數據可用時渲染該組件,然后將其作為道具從父級傳遞到該組件,這將確保單個渲染
這里的問題可能是, fetch
需要花費一些時間來從服務器加載數據,因此DOM在該時間內加載,並且一旦將fetch
數據重新帶回就進行了更新。
要確保組件僅加載一次,請添加一個新的狀態變量isLoaded
或其他內容,並將其設置為false。 然后, fetch
完成后,將其設置為true
,並且僅在isLoaded
為true時才呈現組件。
這是一個代碼示例:
constructor(props){
super(props);
this.state = {
isLoaded: false,
category: {
'published_projects':[{'images':[]}],
'unpublished_projects':[{'images':[]}],
}
};
}
componentWillMount(){
fetch(route('api.categories.show', {'slug':this.props.match.params.slug}))
.then(response => response.json())
.then(data => {this.setState({category: data, isLoaded: true});});
}
最后,在render
方法中,只需執行一個if (this.state.isLoaded) { return(...); } else { return <p>Loading categories...</p>; }
if (this.state.isLoaded) { return(...); } else { return <p>Loading categories...</p>; }
在數據返回之前,沒有辦法延遲對render 方法的調用 。
在執行渲染之前,組件不會等待異步操作完成。 因此,您至少要進行2次渲染調用:
但是,您可以做的是通過返回null
防止在第一次渲染調用期間將組件添加到DOM中。
您可以通過在狀態中添加一個加載標志,然后在數據返回時翻轉它來實現此目的……然后將render方法修改為如下所示:
render() {
if (this.state.loading) return (null);
return (
<div className="w980px center">
...
</div>
);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.