使用API​​调用更新React-Redux中的组件状态

问题描述:

我试图设置一个React应用程序,其中单击某个组件中的地图标记时,将使用数据库中的数据重新呈现页面上的另一个组件并更改URL。它工作,有点,但不好。 我无法弄清楚从Redux获取状态并从API获取响应是否适合React生命周期。使用API​​调用更新React-Redux中的组件状态

有两个相关的问题:

优先:注释掉的行“//APIManager.get()......”不工作,但就行了黑客攻击,一起版本在它下面。

SECOND:我在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." 
+1

它使你的数据库无限通话,因为你调用'componentDidUpdate'的API,然后从API的响应,这会导致'componentDidUpdate'钩再次触发重新设置状态。 –

我没有看到任何Redux都可以在你的代码中使用。如果您计划使用Redux,则应将所有API逻辑移动到动作创建者处,并将API响应存储在Redux Store中。我知道你现在正在快速建立原型。 :)

您的无限循环是由于您选择了错误的生命周期方法而引起的。如果使用componentDidUpdatesetState,它将再次导致componentDidUpdate方法被调用等等。无论何时更新组件,您基本上都会进行更新,如果这是有道理的。 :D

在发送API调用之前,您可以随时检查新的props.params与以前有没有(导致API调用)的不同。您会收到旧的道具并声明该功能的参数。

https://facebook.github.io/react/docs/react-component.html#componentdidupdate

但是,如果你已经决定使用终极版,我想可能是逻辑移动到行动的创建者,存储在你的终极版商店这种反应,只是使用这些数据在connect

+0

我正在使用Redux,因此页面左侧的组件可以与右侧的组件共享数据。我试图获得正确组件的状态来更新和更改URL,而左侧组件保持不变。 –

+0

正如我所说,如果你想使用componentDidUpdate,你需要检查你是否真的需要再次执行API调用。否则,你正在循环。如果你正确使用Redux,你不会有'setState'调用你,你不会面临这个问题。另外,我没有在你的例子中看到“左”组件,所以我不能进一步帮助你。 – CharlieBrown

+0

你说得对,我不需要在那里执行API调用。我将它移出到另一个组件中,并且它工作正常。我在下面的评论中解决了这两个组件,但我仍然不确定是否最好使用Redux实践。 –

尝试使API调用componentDidMount

componentDidMount() { 
    // make your API call and then call .setState 
} 

做那,而不是的componentDidUpdate内。

有很多方法可以在您的React应用程序内构建您的API调用。例如,看看这篇文章:React AJAX Best Practices。如果该链接被打破,它概述了一些想法:

Root Component

This is the simplest approach so it's great for prototypes and small apps.

With this approach, you build a single root/parent component that issues all your AJAX requests. The root component stores the AJAX response data in it's state, and passes that state (or a portion of it) down to child components as props.

由于这是外部的问题的范围,我将离开你到了一些研究,但对管理国家的一些其他方法和异步API调用涉及像Redux这样的库,它现在是React的事实上的状态管理器之一。

顺便说一下,你的无限调用来自于这样一个事实,即当你的组件更新时,它会进行一次API调用,然后调用setState再次更新组件,使你陷入无限循环。

+0

我试过componentDidUpdate(),但它导致无限的GET请求相同的问题 –

+0

@crashspringfield你的意思是'componentDidMount'? 'componentDidUpdate'似乎是无限请求的错误。 –

+0

对不起,我感到困惑。 componentDidMount呈现一次。问题是我有一个左侧和右侧组件,我希望左侧组件控制右侧组件的状态。如果我使用componentDidMount,它会呈现一次,但不会响应左侧组件的更改。 –

我不能帮助的第一个问题,因为我不知道这个APIManager的论据应该是什么。

第二个问题是您在“componentDidUpdate()”中执行API请求的结果。这基本上是发生了什么:

  1. REDX中的某些状态更改。
  2. 远足收到新的道具(或其状态变化)。
  3. 远足根据新的道具呈现。
  4. Hike现在已更新并调用您的“componentDidUpdate”函数。
  5. componentDidUpdate进行API调用,当响应返回时,它触发setState()。
  6. 远足的内部状态改变时,触发该组件的更新(!) - >转到步骤2

当你点击一个链接到另一个页面上,无限循环继续,后最后一次由Hike更新触发的API调用已解决,您再次调用“setState”,它现在尝试更新一个不能装配更长的组件的状态,因此发出警告。

The docs解释这个真的很好我发现,我会给那些彻底的阅读。

还在搞清楚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()”得到搬回行动做在其他设备一切,让我不再担心翘尾因素组件重新无限呈现自己。

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)), 
    } 
} 

看你的代码,我想我会的建筑师也略有不同

几件事情:

  1. 尝试移动API调用和获取数据到Redux行动。由于API获取是异步的,我认为这是最好使用Redux Thunk

例如:

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像任何其他行动的创建者。

  1. 既然你有一条路径/hike/:hikeId我假设你也在更新路线。所以如果你想让人们预订标记并保存并且url .../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) 
    } 
}