何时使用 Flux 加载异步关系
When to load an async relation with Flux
假设您有一个显示宠物资源的宠物组件。您还通过 ownerId 属性拥有属于 pet 的所有者资源。您应该在 Pet 组件的哪个位置加载所有者资源?
const Pet = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId)
};
},
componentDidMount() {
PetStore.addChangeListener(this._onChange);
},
componentWillUnmount() {
PetStore.removeChangeListener(this._onChange);
},
render() {
var owner; // where should I initiate the ajax request to get owner?
return (
<p>{owner.name}</p>
);
},
onChange() {
this.setState({
pet: PetStore.get(this.props.petId)
});
}
});
React documentation recommends 在 componentDidMount
中执行此操作。
正如 Thilo 提到的,React 文档建议您在 componentDidMount
中异步加载数据。在 componentDidMount
而不是 componentWillMount
上执行此操作的原因是,如果您 运行 在服务器上对初始页面加载做出反应,那么 componentWillMount
将被执行,而您没有想在服务器上进行 Ajax 调用。
话虽如此,我建议您将数据获取逻辑移至另一个组件(在 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
组件就不必担心异步数据获取,因为在数据存在之前它不会被渲染。
假设您有一个显示宠物资源的宠物组件。您还通过 ownerId 属性拥有属于 pet 的所有者资源。您应该在 Pet 组件的哪个位置加载所有者资源?
const Pet = React.createClass({
getInitialState() {
return {
pet: PetStore.get(this.props.petId)
};
},
componentDidMount() {
PetStore.addChangeListener(this._onChange);
},
componentWillUnmount() {
PetStore.removeChangeListener(this._onChange);
},
render() {
var owner; // where should I initiate the ajax request to get owner?
return (
<p>{owner.name}</p>
);
},
onChange() {
this.setState({
pet: PetStore.get(this.props.petId)
});
}
});
React documentation recommends 在 componentDidMount
中执行此操作。
正如 Thilo 提到的,React 文档建议您在 componentDidMount
中异步加载数据。在 componentDidMount
而不是 componentWillMount
上执行此操作的原因是,如果您 运行 在服务器上对初始页面加载做出反应,那么 componentWillMount
将被执行,而您没有想在服务器上进行 Ajax 调用。
话虽如此,我建议您将数据获取逻辑移至另一个组件(在 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
组件就不必担心异步数据获取,因为在数据存在之前它不会被渲染。