2

In this.state.companiesIncome, I have an array of 50 objects that have a {value and date}, but when I'm trying to console.log(this.state.companiesIncome[2].value) I'm receiving TypeError: Cannot read property '2' of null. What I'm doing wrong?

import React, { Component } from 'react'
import './CompanyTotalIncome.css'
import axios from 'axios'

class CompanyIncome extends Component {
  constructor(props) {
    super(props)
    this.state = {
      companyID: props.companyID,
      companiesIncome: null
    }
  }

  componentWillMount() {
    axios.get(`https://API/${this.state.companyID}`).then(res => {
      this.setState({ companiesIncome: res.data.incomes })
    })
  }

  render() {
    console.log(this.state.companiesIncome[2].value)
    return (
      <>
        <th>Total income</th>
      </>
    )
  }
}

export default CompanyIncome

1
  • componentWillMount is a lifecycle method that will get called when your component mounts, however, your API call is asynchronous, so by the time you get to render, your API may still be going and your state is null, so you need to handle this better, for example render a list of items only if (this.state.companiesIncome && this.state.companiesIncome.length > 0) Commented Dec 25, 2019 at 14:28

3 Answers 3

1

componentWillMount has been deprecated.

Use componentDidMount instead of componentWillMount to make the AJAX Call.

componentDidMount() {
  axios.get(`https://API/${this.state.companyID}`).then(res => {
    this.setState({ companiesIncome: res.data.incomes });
  });
}

And place a check before using this.state.companiesIncome

this.state.companiesIncome && console.log(this.state.companiesIncome[2].value);

You're seeing the error before the axios.get will fire an AJAX call which is asynchronous by nature. And your render method will anyway get called before componentDidMount. So by the time the log gets called, this.state.companiesIncome would still be null.

Once componentDidMount is called, the API data is fetched and setState is called, it will trigger a re-render and the render method will be called again. This time with companiesData set. So it would work as expected.

Here's a Working Code Demo Example for your ref.

Sign up to request clarification or add additional context in comments.

Comments

0

Because at the initial state your companiesIncome is null, companiesIncome[2] will throw an error. To avoid this, you can do the following:

this.state.companiesIncome && this.state.companiesIncome[2].value

This way, it will be printed only if you have a valid companiesIncome value. To improve it a bit you can also write:

(this.state.companiesIncome && this.state.companiesIncome.length >= 2) && this.state.companiesIncome[2].value

If you only want to log it to the console, do the following:

console.log(this.state.companiesIncome ? this.state.companiesIncome[2].value : 'companiesIncome is null');

Comments

0

The fact is that React starts rendering at the beginning. You can look in JSFiddleExample, in the console you will see a render at the beginning, then componentDidMount. Therefore, you must add validation. this.state.companiesIncome! == null. When you will change the state in the componentDidMount, the render starts again with your data. You can read about React Lifecycle in React documentation

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.