2

I'm new to react and I have a question about a best practice that sees me make a mistake . I call an API to retrieve information and modify an array in the state once the response is returned by the API. In the "render" I have to retrieve the information from this array (when it is completed) or it sends me back an error because the array is empty when the render is initialized.

class MyClass extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      activeIndex: 0,
      items: []
    }
  }

  componentDidMount() {
    axios
      .get(`API_ADDRESS`, {
        headers: {
          Authorization: `Token XXX`,
        },
      })
      .then(function(response) {
        this.setState({
          items: response.results,
        })
      })
      .catch(error => {
        notification.warning({
          message: error.code,
          description: error.message,
        })
      })
  }

  changeDialog = (e, index) => {
    e.preventDefault()
    this.setState({
      activeIndex: index,
    })
  }

  render() {
    const { activeIndex, items } = this.state
    const {
      first_name: firstName,
      last_name: lastName,
      phone,
      email,
      address,
    } = items[activeIndex]

The error indicates :

TypeError: _items$activeInde is undefined

How can I solve this error related to data loading? (trying to keep the destrying elements method)

Thanks a lot Eliott

1
  • I'd not say that this in any way specific to react. The problem is that you blindly try to destructure items[activeIndex] without ensuring that you don't get a null-reference exception. Commented Oct 2, 2019 at 6:20

3 Answers 3

1

Because API that you fetch from server is async. The first time render of Component, data that you setState in axios still not yet updated, it just updated when Component render the second time.

So you must check state in render Component like this to make sure that if activeIndex is defined then declare variable with items[activeIndex] :

activeIndex && const {
      first_name: firstName,
      last_name: lastName,
      phone,
      email,
      address,
} = items[activeIndex]

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

Comments

0

Two issues:

  • beware of this inside the Promise returned by axios. You use function(){} so the this inside is not the component's instance. Change it to an arrow function.
  • add a guard so you won't destructure undefined when activeIndex points to an item element that is not there (which happens in the initial loading before the axios fetches the data).

Fix:

// ... (code not shown remains unmodified)
componentDidMount() {
  axios
    .get(`API_ADDRESS`, {
      headers: {
        Authorization: `Token XXX`,
      },
    })
    .then(response => {                                             // changed this line
      this.setState({
        items: response.results,
      })
    })
// ... (code not shown remains unmodified)

render() {
  const { activeIndex, items } = this.state
  if (!items[activeIndex]) {                                        // added this line
    return <div>Hold tight while items are being fetched...</div>;  // added this line
  }                                                                 // added this line
  const {
    first_name: firstName,
// ... (code not shown remains unmodified)

Comments

0

just change your component like so:

  constructor(props) {
    super(props)
    this.state = {
      activeIndex: 0,
      items: [],
      isFetching: false
    }
  }

  componentDidMount() {
    // staring your fetching
    this.setState({isFetching: true});
    axios
      .get(`API_ADDRESS`, {
        headers: {
          Authorization: `Token XXX`,
        },
      })
      .then(function(response) {
        // finish fetching when your response is ready :)
        this.setState({
          items: response.results,
          isFetching: false
        });
      })
      .catch(error => {
        // finish fetchnig
        this.setState({isFetching: false})
        notification.warning({
          message: error.code,
          description: error.message,
        })
      })
  }

  changeDialog = (e, index) => {
    e.preventDefault()
    this.setState({
      activeIndex: index,
    })
  }

  render() {

    // if your component is while fetching shows a loading to the user
    if(this.state.isFetching) return <div>Loading...</div>;
    // if there is no results shows a msg to the user
    if(this.state.items.length === 0) return <div>there is not items!!!</div>

    const { activeIndex, items } = this.state
    const {
      first_name: firstName,
      last_name: lastName,
      phone,
      email,
      address,
    } = items[activeIndex]

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.