[英]Updating component state in React-Redux with API calls
我试图建立一个React应用程序,在其中单击一个组件中的地图标记会使用数据库中的数据重新呈现页面上的另一个组件并更改URL。 它可以工作,但效果不佳。 我在弄清楚如何从Redux获取状态以及如何从API获取响应的过程中无法适应React的生命周期。
有两个相关的问题:
首先:注释掉的行“ //APIManager.get()......”不起作用,但下面一行的被黑在一起的版本却起作用。
第二:我在console.log()-响应中的行将无限记录并向数据库发出无限GET请求。
这是我下面的组件:
class Hike extends Component {
constructor() {
super()
this.state = {
currentHike: {
id: '',
name: '',
review: {},
}
}
}
componentDidUpdate() {
const params = this.props.params
const hack = "/api/hike/" + params
// APIManager.get('/api/hike/', params, (err, response) => { // doesn't work
APIManager.get(hack, null, (err, response) => { // works
if (err) {
console.error(err)
return
}
console.log(JSON.stringify(response.result)) // SECOND
this.setState({
currentHike: response.result
})
})
}
render() {
// Allow for fields to be blank
const name = (this.state.currentHike.name == null) ? null : this.state.currentHike.name
return (
<div>
<p>testing hike component</p>
<p>{this.state.currentHike.name}</p>
</div>
)
}
}
const stateToProps = (state) => {
return {
params: state.hike.selectedHike
}
}
export default connect(stateToProps)(Hike)
另外:当我单击页面上的链接以转到另一个URL时,出现以下错误:
"Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op."
查看您的代码,我想我会稍微不同地设计它
一些事情:
例:
function fetchHikeById(hikeId) {
return dispatch => {
// optional: dispatch an action here to change redux state to loading
dispatch(action.loadingStarted())
const hack = "/api/hike/" + hikeId
APIManager.get(hack, null, (err, response) => {
if (err) {
console.error(err);
// if you want user to know an error happened.
// you can optionally dispatch action to store
// the error in the redux state.
dispatch(action.fetchError(err));
return;
}
dispatch(action.currentHikeReceived(response.result))
});
}
}
您也可以像对待其他动作创建者一样对待fetchHikeById,将调度映射到fetchHikeById的道具。
/hike/:hikeId
我假设您也在更新路由。 因此,如果您希望人们进行书签并保存并添加网址.../hike/2
或返回它。 您仍然可以将提取内容放入Hike组件中。 您执行fetchHikeById操作的生命周期方法是。
componentDidMount() {
// assume you are using react router to pass the hikeId
// from the url '/hike/:hikeId'
const hikeId = this.props.params.hikeId;
this.props.fetchHikeById(hikeId);
}
componentWillReceiveProps(nextProps) {
// so this is when the props changed.
// so if the hikeId change, you'd have to re-fetch.
if (this.props.params.hikeId !== nextProps.params.hikeId) {
this.props.fetchHikeById(nextProps.params.hikeId)
}
}
我看不到代码中使用了Redux。 如果您打算使用Redux,则应将所有API逻辑移至操作创建者中,并将API响应存储在Redux Store中。 我了解您现在正在快速制作原型。 :)
导致无限循环的原因是您选择了错误的生命周期方法。 如果使用componentDidUpdate
和setState
,它将再次导致componentDidUpdate
方法被调用,依此类推。 基本上,只要有任何意义,只要有组件更新,您就在更新。 :D
在发送API调用之前,您始终可以检查props.params
您拥有的新props.params
是否与之前(引起API调用)的props.params
不同。 您会收到旧的道具和状态作为该函数的参数。
https://facebook.github.io/react/docs/react-component.html#componentdidupdate
但是,如果您决定使用Redux,我可能会将该逻辑移至动作创建者,将该响应存储在Redux Store中,并仅在connect
使用该数据。
尝试在componentDidMount
进行API调用:
componentDidMount() {
// make your API call and then call .setState
}
而不是在componentDidUpdate
内部执行此操作。
有很多方法可以在React应用程序内部构建API调用。 例如,看一下这篇文章: React AJAX Best Practices 。 万一链接断开,它概述了一些想法:
根成分
这是最简单的方法,因此非常适合原型和小型应用程序。
使用这种方法,您可以构建一个发布所有AJAX请求的根/父组件。 根组件以其状态存储AJAX响应数据,并将该状态(或状态的一部分)向下传递给子组件作为道具。
由于这超出了问题的范围,我将进行一些研究,但是其他一些用于管理状态和异步API调用的方法涉及到Redux之类的库,它是React的事实上的状态管理器之一现在。
顺便说一句,您的无限调用来自以下事实:当您的组件更新时,它会进行API调用,然后调用setState
来再次更新组件,从而使您陷入无限循环。
我无法解决的第一个问题,因为我不知道此APIManager的参数应该是什么。
第二个问题是您在“ componentDidUpdate()”中执行API请求的结果。 本质上就是这样:
当您单击指向另一页的链接时,无限循环将继续,解决由Hike更新触发的最后一个API调用解决后,您将再次调用“ setState”,该操作现在尝试更新不再存在的状态-安装的组件,因此发出警告。
我发现文档非常好地解释了这一点,我将对它们进行彻底的阅读。
仍在弄清楚Redux的流程,因为当我将API请求从Hike组件移到正在侦听的组件时,它解决了问题。 现在,一旦数据库信息赶上了重新路由和重新渲染,Hike组件便会监听并重新渲染。
Hike.js
class Hike extends Component {
constructor() {
super()
this.state = {}
}
componentDidUpdate() {
console.log('dealing with ' + JSON.stringify(this.props.currentHike))
}
render() {
if (this.props.currentHike == null || undefined) { return false }
const currentHike = this.props.currentHike
return (
<div className="sidebar">
<p>{currentHike.name}</p>
</div>
)
}
}
const stateToProps = (state) => {
return {
currentHike: state.hike.currentHike,
}
}
并且“ this.props.currentHikeReceived()”移回了在其他组件中执行所有操作的动作,因此我不再需要担心Hikes组件会无限地重新渲染自身。
Map.js
onMarkerClick(id) {
const hikeId = id
// Set params to be fetched
this.props.hikeSelected(hikeId)
// GET hike data from database
const hack = "/api/hike/" + hikeId
APIManager.get(hack, null, (err, response) => {
if (err) {
console.error(err)
return
}
this.props.currentHikeReceived(response.result)
})
// Change path to clicked hike
const path = `/hike/${hikeId}`
browserHistory.push(path)
}
const stateToProps = (state) => {
return {
hikes: state.hike.list,
location: state.newHike
}
}
const dispatchToProps = (dispatch) => {
return {
currentHikeReceived: (hike) => dispatch(actions.currentHikeReceived(hike)),
hikesReceived: (hikes) => dispatch(actions.hikesReceived(hikes)),
hikeSelected: (hike) => dispatch(actions.hikeSelected(hike)),
locationAdded: (location) => dispatch(actions.locationAdded(location)),
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.