Cannot access array data in react.js


#1

I´m trying to work with an array, set on the status of the component after using axio to get the data from an url.

import React, { Component } from 'react';
import axios from 'axios';

class Articulo extends Component {

  constructor(props) {
    super(props);
    this.state = {
      url : 'http://aldeasinfantiles-wp.lin3sdev.com/wp-json/wp/v2/posts?slug=' + this.props.match.params.slug
    }
  }

  state = {
    articles: [],
  };

  componentDidMount() {
    axios.get(this.state.url).then(res => {
      this.setState({ articles: res.data});
    });
  };

  render() {
    return (<h2>Este es el slug del artículo: {console.log(this.state.articles)}</h2>)
  }

}

export default Articulo;

this.state.articles is just the full array, but I don't know how to access to the data. I have also tried this:

render() {
    return(<div>{this.state.articles.map( article => <article><h1>{article.title}</h1></article>) }</div>)
}

#2

Hi,

this is likely being caused by the fact that you are fetching the data from a remote endpoint (an async action) and the request hasn't completed by the time that your component attempts to render itself.

You can get around this by adding a check inside your render method:

render() {
  if (this.state.articles === null) return null;
  
  return(
    <div>
      { this.state.articles.map( article => <article><h1>{article.title}</h1></article>) }
    </div>
  );
}

Returning null will cause nothing to be rendered initially, then when the Ajax request completes, you update the state (as you are doing) and the component is rendered with the correct data.

Also, you shouldn't be declaring state outside the constructor:

state = {
  articles: [],
};

#3

You need to process the data. There is no data processing to turn this data into anything but exactly what you're getting from the endpoint.

Also, under absolutely no circumstances, should you be console logging from your JSX. It's ok to console log from within the render function for debugging and anything in the JSX is also going to be accessible from within the render function.

Try this:

render() {
  return (
    <h2>Este es el slug del artículo:
      {this.state.articles.join(',')}
    </h2>
  )
}

#4

Thanks for your reply. I have tried bouth solutions but in both of them I receive the following error:

TypeError: this.state.articles is undefined

What I have checked if that if I do console.log(this.state.articles) I get two responses. The first sends me undefined, the second sends me an array with all the data.


#5

That's because you're assigning the state twice -- as a class property (which is currently just a proposal and gets transpiled to a regular assignment inside the constructor) and then actually inside the constructor, which would overwrite the class property assignment. So on initial render, articles is undefined.

BTW unless the URL gets dynamically changed, there's no need to assign it to the state in the first place... just something like this would work as well:

this.url = 'http://...' + this.props.match.params.slug

#6

TypeError: this.state.articles is undefined

Sorry, I missed this bit. You can either use a constructor or a class property, not both.

Remove the class property and add it to your constructor:

constructor(props) {
    super(props);
    this.state = {
      url : `http://aldeasinfantiles-wp.lin3sdev.com/wp-json/wp/v2/posts?slug=${this.props.match.params.slug}`,
      articles: []
    }
  }

#7

Thanks for the advice, it's being hard to understand. The rul is written that way because it changes depending of the slug variable. It seems to work fine wth ComponentDigMount().


#8

But that slug variable comes from the props; so when you pass a different slug prop to the component, the state wouldn't even update with the new URL. What you might do though is define a getter like so:

class Articulo extends Component {

  state = {
    articles: [],
  }

  get url() {
    return 'http://aldeasinfantiles-wp.lin3sdev.com/wp-json/wp/v2/posts?slug=' + this.props.match.params.slug
  }

  componentDidMount() {
    axios.get(this.url).then(res => {
      this.setState({ articles: res.data })
    });
  }

  render() {
    return (
      <div>
        {this.state.articles.map(article => (
          <article>
            <h1>{article.title}</h1>
          </article>
        ))}
      </div>
    )
  }
}

You might also do that AJAX call when componentDidUpdate() though to fetch new articles after the component received new props...


#9

Tried your code but it throws me the same error...I´m doing almost the same thing I do in another component, where everything is fine, so I cannot understand what is happeing.

As you said the url won't change in the props, so, should I use it in the state? I´m totally lost and can't understand why the data is stored in article array, but can't access to it.


#10

Finally solve this way!

class Articulo extends Component {

  constructor(props) {
    super(props);
  }

  state = {
    articles: [],
    url: 'http://aldeasinfantiles-wp.lin3sdev.com/wp-json/wp/v2/posts?slug=' + this.props.match.params.slug
  };

  componentDidMount() {
    axios.get(this.state.url).then(res => {
      this.setState({ articles: res.data});
    });

  };

  render() {
        return (<div className='articles-wrapper'>
        {
          this.state.articles.map((articulo) => {
            return <h1>{articulo.title.rendered}</h1>
          })
        }
        </div>)

    }

}

#11

I actually meant the exact opposite... ^^ there's no setState({ url: someNewUrl }) anywhere in your code, so there's no need to have it in the state; new URLs will always come from the props. Anyway glad you got it working! :-)