正如Thilo提到的,React文檔建議您在componentDidMount
中執行異步數據加載。在componentDidMount
而不是componentWillMount
上執行此操作的原因是,如果您在服務器上爲初始頁面加載運行React,並且您不想在服務器上進行Ajax調用,componentWillMount
將會得到執行。這就是說,我建議你將數據提取邏輯移動到另一個組件(在React世界中通常稱爲視圖控制器)。這意味着,您不必讓一個組件執行數據提取和渲染,而是有兩個組件。一個責任是獲取數據,另一個是呈現數據。您將該數據作爲道具從視圖控制器組件傳遞給渲染組件。一個小例子是:
var Pet = React.createClass({
render() {
return (
<p>Pet: {this.props.pet.name}</p>
<p>Owner: {this.props.owner.name}</p>
);
}
});
var PetController = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId),
owner: null
};
},
componentDidMount() {
OwnerStore.getOwner(this.state.pet.ownerId)
.then(owner => this.setState({owner: owner}));
},
render() {
return <Pet pet={this.state.pet} owner={this.state.owner} />;
}
});
什麼這種分離給你更簡單和更集中的組件。 Pet
組件不必擔心在何處獲取數據,並且可以在其他情況下重用該組件,而其他情況下您可以從PetStore
以外的某個位置獲取數據。而PetController
組件只關注數據獲取和狀態管理。爲了使Pet
組件更簡單,您可以避免渲染它,直到獲取所有者。喜歡的東西:
var PetController = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId),
owner: null
};
},
componentDidMount() {
OwnerStore.getOwner(this.state.pet.ownerId)
.then(owner => this.setState({owner: owner}));
},
render() {
if (this.state.pet && this.state.owner) {
return <Pet pet={this.state.pet} owner={this.state.owner} />;
} else {
return <div className="spinner" />;
}
}
});
然後你Pet
組件不必擔心異步數據讀取,因爲它不會渲染,直到數據是存在的。
這很有道理。我注意到你從你的調用OwnerStore.getOwner返回一個承諾。您不會在動作創建者中發起請求,然後聽取商店更新? – niftygrifty
當然,你可以做到這一點,但這往往會使商店層有點混亂,因爲你必須跟蹤所有組件目前感興趣的內容。而且你還必須跟蹤改變的時間。我發現僅僅告訴我的組件改變了一些東西就更簡單了,並讓組件從商店中再次提取需要的東西。但並非所有人都同意我的觀點,人們對Flux的理解似乎有很多不同。 –
只是一個額外的評論,因爲我已經被它燒了幾次..任何時候你執行一個異步操作,然後更新狀態,請確保你在調用'setState'之前檢查'this.isMounted()=== true'。虛擬DOM節點超過了真正的DOM節點,如果組件未安裝,它將會引發異常 –