我有一个React组件,显示有关实体的信息。实体的id通过属性传入。该组件在" componentDidMount"中启动AJAX调用。在调用完成/失败时获取实体并更新状态。
这种方法很好,只是当实体ID发生变化时,组件不会获取新数据(通过道具)。
我已尝试在" componentWillReceiveProps"但在那个阶段,组件仍然具有旧属性集。我必须将nextProps传递给AJAX调用方法,这看起来并不正确。
让组件异步更新其状态以响应属性更改的最佳/最干净方法是什么?
答案 0 :(得分:9)
我也是新手,所以Flux架构对我来说有点吓人。我正如你所说的那样,使用componentWillMount
通过AJAX加载初始数据,然后使用componentWillReceiveProps
加载nextProps
,如果/当道具发生变化时再次加载新数据:
var Table = React.createClass({
getInitialState: function() {
return { data: [] };
},
componentWillMount: function(){
this.dataSource();
},
componentWillReceiveProps: function(nextProps){
this.dataSource(nextProps);
},
dataSource: function(props){
props = props || this.props;
return $.ajax({
type: "get",
dataType: 'json',
url: '/products?page=' + props.page + "&pageSize=" + props.pageSize
}).done(function(result){
this.setState({ data: result });
}.bind(this));
},
render: function() {
return (
<table className="table table-striped table-bordered">
<Head />
<Body data={this.state.data}/>
</table>
);
}
});
答案 1 :(得分:0)
钩子componentWillMount
和componentWillReceiveProps
自从React v16.3.0(source)开始被弃用。
componentDidMount
钩子处进行 AJAX请求。如果您希望在更改一些道具后刷新数据,则必须使用componentDidUpdate
挂钩。
但是您还必须处理另外三个生命周期挂钩,以避免启动请求/更新的无限循环。假设您希望基于props.category
的更改来更新帖子列表:
state
应该具有两个属性,category
和currentCategory
,在组件的构造函数上设置null; getDerivedStateFromProps
需要从新的state.category
更新props.category
; shouldComponentUpdate
需要比较state.category
和state.currentCategory
,以确定是否应更新组件; getSnapshotBeforeUpdate
是确定componentDidUpdate
是否应发出AJAX请求或更改state.currentCategory
值并完成更新周期的必要条件。完整的代码如下(source):
import React, { Component, Fragment } from 'react';
import axios from "axios";
class Post extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
category: null,
currentCategory: null
};
this._createMarkup = this._createMarkup.bind();
}
static getDerivedStateFromProps(props, state) {
if (props.category !== state.category) {
return {
category: props.category
};
}
return null;
}
componentDidMount() {
this._fetchData();
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.currentCategory !== nextState.category;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
return prevState.currentCategory !== prevState.category;
}
componentDidUpdate(prevProps, prevState, dataDidFetch) {
// dataDidFetch is returned by getSnapshotBeforeUpdate
if (dataDidFetch) {
this.setState({
currentCategory: this.state.category
});
} else {
this._fetchData();
}
}
_fetchData() {
const category = this.state.category;
axios.get(`/some/api/endpoint?category=${category}`).then(posts => {
this.setState({
posts: posts.data
});
});
}
_createMarkup(html) {
return { __html: html };
}
render() {
return (
<Fragment>
{this.state.posts.map(post => (
<article className="post" key={post.id}>
<h2>{post.title.rendered}</h2>
<div dangerouslySetInnerHTML={this._createMarkup( post.content.rendered )} />
<p className="post-link">
<a href="{post.resource_link_url}">{post.resource_link_label}</a>
</p>
</article>
))}
</Fragment>
);
}
}
export default Post;