2

I'm building a small app that consumes a REST api. I'm running into problems displaying information inside arrays of objects, see code below:

actions.js

import axios from 'axios'

function fetchService () {
  return axios.get('http://localhost:5000/ldbws-rest-proxy/v0.1/departure-board/IPS')
    .then(function (response) {
      return {
        service: response.data.trainServices[0]
      }
    })
    .catch(function (response) {
      console.log(response)
    })
}

export default fetchService

train_service.js

import fetchService from '../actions'

import DepartureTime from './departure_time'
import OriginStation from './origin_station'

var TrainService = React.createClass ({
  getInitialState () {
    return {
      service: []
    }
  },
  componentDidMount () {
    fetchService()
      .then(function (dataObj) {
        this.setState({
          service: dataObj.service
        })
      }.bind(this))
  },
  render () {
    return (
      <section>
        <DepartureTime time={this.state.service.std} />
        <OriginStation name={this.state.service.origin[0].crs} />
      </section>
    )
  }
})

export default TrainService

JSON sample (response.data.trainServices[0])

{
  "destination": [
    {
      "crs": "CBG",
      "locationName": "Cambridge"
    }
  ],
  "etd": "On time",
  "operator": "Greater Anglia",
  "operatorCode": "LE",
  "origin": [
    {
      "crs": "IPS",
      "locationName": "Ipswich"
    }
  ],
  "serviceID": "ILZn7gyLj+eoZZfyaFlP0w==",
  "std": "12:20"
}

The problem is that <OriginStation name={this.state.service.origin[0].crs} /> throws an error:

TypeError: undefined is not an object (evaluating 'this.state.service.origin')

I'm not sure why this isn't working, if I do console.log(dataObj.service.origin[0].crs) inside componentDidMount it outputs fine. I think it's something to do with the origin array...

Any help appreciated.

EDIT:

Screenshot of the state in the Chrome Inspector:

Screenshot

1
  • Did you check your actual TrainService state with something like React Developer Tools in Chrome browser? Commented Feb 1, 2017 at 12:09

2 Answers 2

2

It's because your TrainService render method calls earlier than fetchService promise resolves. Easiest way to fix your error is wait for fetchService updates service state:

var TrainService = React.createClass ({
  getInitialState () {
    return {
      service: null
    }
  },
  componentDidMount () {
    fetchService()
      .then(function (dataObj) {
        this.setState({
          service: dataObj.service
        })
      }.bind(this))
  },
  render () {
    if (this.state.service === null)
      return null;
    return (
      <section>
        <DepartureTime time={this.state.service.std} />
        <OriginStation name={this.state.service.origin[0].crs} />
      </section>
    )
  }
})
Sign up to request clarification or add additional context in comments.

2 Comments

Thank-you, I hadn't considered that. Is there a way that React can handle this for me e.g. by using componentWillMount or something similar that can wait for things like this?
@lkemitchll I don't think so, because you (but not React core) calls async method. It's ok, because async request not freeze whole application. You can set some data in initial state (componentDidMount) to show user some data before it really loads
0

fetchService is making an async call. So when componentDidMount is run, it will make an async call and proceeds to render.

When the render function is executed for the first time you state is not populated with the data and it has an empty array for this.state.service, from getInitialState.

If you are writing console.log inside componentDidMount as

componentDidMount () {
    fetchService()
        .then(function (dataObj) {
             console.log(dataObj.service.origin[0].crs)
             this.setState({
                  service: dataObj.service
             })
        }.bind(this))
},

the console.log gets executed only when the async call has succeeded, so the data is available.

To solve this either don't render the component until the state data is ready.

render() {
    this.state.service.length == 0 && return null
    ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.