[英]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.