1

I want to fetch JSON data, store it in state and then pass it to component through props. In the component I want to use the map function but it shows me this error : TypeError: this.props.dataQueries.map is not a function. This is my code:

class App extends Component {
    constructor(props) {
      super(props);
      this.state = {
        dataQueries: ''
      }
    }

  fetchData() {
    fetch('https://jsonplaceholder.typicode.com/posts', {method: "GET"}).
    then(res => res.json()).
    then(result => this.setState({ dataQueries: result }));
  }

  componentWillMount() {
    this.fetchData();
  }

  render() {
    return (
      <div>
        <ShowPosts dataQueries={ this.state.dataQueries } />
      </div>
    );
  }
}

And this is my component :

class ShowPosts extends Component {
  render() {
    return (
      <div>
      {
      this.props.dataQueries.map((query, index) => {
        return index;
      })
      }
      </div>
    );
  }
}
5
  • 1
    dataQueries might be a string, if it came from a request. Parse it before considering it as an array. Commented Feb 27, 2018 at 12:45
  • then(result => this.setState({ dataQueries: JSON.parse(result) })); Commented Feb 27, 2018 at 12:46
  • 1
    initialize dataQueries as an array [] instead of empty string '' Commented Feb 27, 2018 at 12:46
  • @RajaprabhuAravindasamy: No, that's part of what res.json() does: developer.mozilla.org/en-US/docs/Web/API/Body/json Commented Feb 27, 2018 at 13:29
  • @T.J.Crowder Oh really, I am not aware of that. Thanks for the info though. Commented Feb 27, 2018 at 13:30

2 Answers 2

2

Initially, you're setting dataQueries to ''. Which has no map, as it's a string. It's only an array later, when your fetchData async call has completed. Nothing prevents render from being called before the async call completes.

Initialize it to [] instead, or modify render to avoid trying to use it as an array when it's not an array.

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

Comments

0

You should call this.fetchData() inside componentDidMount() lifecycle method. So when component is mounted only then you update the state with response from API. Also, you should render ShowPosts component when there are posts to render using conditional rendering.

render() {
  return (
    <div>
      {this.state.dataQueries.length && <ShowPosts dataQueries={ this.state.dataQueries } />}
    </div>
  );
}

And your initial dataQueries should be an empty array. dataQueries = []

2 Comments

Avoid introducing any side-effects or subscriptions in this method. For those use cases, use componentDidMount() instead. is mentioned above provided docs link. Calling an external API and setting a callback is kind of a subscription through promise. So componentWillMount and constructor is recommended when state is being set synchronously which is not the case in this scenario.
@falloutcoder: I didn't think that's what they meant by subscription (and I'm not sure I still don't), but here's an even stronger statement in support of using componentDidMount for ajax: reactjs.org/docs/… Since either way there's going to be an extra render, based on that, I would indeed use componentDidMount instead.

Your Answer

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