5

I'm new to react native, and I'm trying to simply iterate through a sample json file but am receiving the error undefined is not a function (evaluating 'this.state.results.map')

I have set the state initially to be an object, so not sure why i am receiving this error.

Here is the JS:

import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View, StyleSheet, TouchableHighlight } from 'react-native';

var REQUEST_URL = 'https://facebook.github.io/react-native/movies.json';

class WPReact extends Component {
  constructor(props) {
    super(props);
    this.state = {results: []};
  }
  componentDidMount() {
    this.fetchData();
  }
  fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          results : { responseData }
        });
      })
      .done();
  }
  render() {
    return this.renderJSON();
  }
  renderJSON() {
    contents = this.state.results.map((item) => {
      <View key={item.movies.title} style={ styles.container }>
        <Text style={styles.title}>
          {item.movies.title}
        </Text>
      </View>
     });
    return (
      <View style={styles.container}>
        {contents}
      </View>
    );
  }
}

var Dimensions = require('Dimensions');

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#FFFFFF',
  },
  textContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 30,
    textAlign: 'center',
    margin: 10,
  },
  text: {
    fontSize: 18,
    paddingLeft: 20,
    paddingRight: 20,
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => WPReact);

EDIT

So i have edited the renderJSON() to and also removed the braces of responseData as you said, as it was already an object:

renderJSON() {

    console.log(this.state.results.description);
    contents = this.state.results.movies.map((item) => {
      <View key={item.title} style={ styles.container }>
        <Text style={styles.title}>
          {item.title}
        </Text>
      </View>
     });
    return (
      <View style={styles.container}>
        {contents}
      </View>
    );
  }

I added a console log to see if i can output some of the data, and i can see the description. The sample JSON i am using is (demo from react):

{
  "title": "The Basics - Networking",
  "description": "Your app fetched this from a remote endpoint!",
  "movies": [
    { "title": "Star Wars", "releaseYear": "1977"},
    { "title": "Back to the Future", "releaseYear": "1985"},
    { "title": "The Matrix", "releaseYear": "1999"},
    { "title": "Inception", "releaseYear": "2010"},
    { "title": "Interstellar", "releaseYear": "2014"}
  ]
}

I can log the description and title. But I am still receiving: ReactNativeJS: undefined is not an object (evaluating 'this.state.results.movies.map')

And if I try logging console.log(this.state.results.movies[0].title) I am receiving undefined is not an object (evaluating 'this.state.results.movies[0]')

fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        console.log(responseData);
        this.setState({
          results : responseData
        });
      })
      .done();
  }

console.log(responseData) shows:

03-29 13:49:53.028  3062  4143 I ReactNativeJS: { title: 'The Basics - Networking',
03-29 13:49:53.028  3062  4143 I ReactNativeJS:   description: 'Your app fetched this from a remote endpoint!',
03-29 13:49:53.028  3062  4143 I ReactNativeJS:   movies: 
03-29 13:49:53.028  3062  4143 I ReactNativeJS:    [ { title: 'Star Wars', releaseYear: '1977' },
03-29 13:49:53.028  3062  4143 I ReactNativeJS:      { title: 'Back to the Future', releaseYear: '1985' },
03-29 13:49:53.028  3062  4143 I ReactNativeJS:      { title: 'The Matrix', releaseYear: '1999' },
03-29 13:49:53.028  3062  4143 I ReactNativeJS:      { title: 'Inception', releaseYear: '2010' },
03-29 13:49:53.028  3062  4143 I ReactNativeJS:      { title: 'Interstellar', releaseYear: '2014' } ] }

console.log(this.state.results.movies);

03-29 14:18:05.483  3062  4726 I ReactNativeJS: undefined
03-29 14:18:05.510  3062  4726 I ReactNativeJS: [ { title: 'Star Wars', releaseYear: '1977' },
03-29 14:18:05.510  3062  4726 I ReactNativeJS:   { title: 'Back to the Future', releaseYear: '1985' },
03-29 14:18:05.510  3062  4726 I ReactNativeJS:   { title: 'The Matrix', releaseYear: '1999' },
03-29 14:18:05.510  3062  4726 I ReactNativeJS:   { title: 'Inception', releaseYear: '2010' },
03-29 14:18:05.510  3062  4726 I ReactNativeJS:   { title: 'Interstellar', releaseYear: '2014' } ]
KT.C
  • 98
  • 1
  • 1
  • 7
  • what happens when you `console.log(this.state.results.movies)`? – Pineda Mar 29 '17 at 13:08
  • I've modified my original post to show you, not sure why there is an undefined there? – KT.C Mar 29 '17 at 13:20
  • This occurs even after you use `JSON.parse` as I suggested in my amended answer? – Pineda Mar 29 '17 at 13:23
  • Ahah you are logging the results before setting them into state! Log them before you call `this.renderJSON` in your `render` method – Pineda Mar 29 '17 at 13:28
  • @Pineda putting `console.log(this.state.results.movies)` in the render method outputs the same as above what i posted, still shows undefined. – KT.C Mar 29 '17 at 13:40
  • You are sure you are only making that one console.log call in render now? – Pineda Mar 29 '17 at 13:47
  • @Pineda - it appears to working now alongside the other answer. Thanks for the help. – KT.C Mar 29 '17 at 14:07
  • What fixed it specifically? – Pineda Mar 29 '17 at 14:15
  • @Pineda i believe it must have been not declaring the states properly in the constructor. – KT.C Mar 29 '17 at 14:31
  • The way you had it initially was fine. Maybe something went out of sync with all the changes. Glad you found a solution. If you found my answer helpful, an up-vote would be greatly appreciated (when you gain enough rep of course :D) – Pineda Mar 29 '17 at 14:35

3 Answers3

7

I see a couple of things you need to change.

Firstly, you need to bind fetchData method when you are using ES6 doing this this.fetchData = this.fetchData.bind(this); in the constructor (look for other ways to do this).

Secondly, map should be applied to this.state.results.movies due this is the array (following your post). this.state.results is not an array, is an object containing an array.

import React, { Component } from 'react';
import { AppRegistry, ListView, Text, View, StyleSheet, TouchableHighlight } from 'react-native';

var REQUEST_URL = 'https://facebook.github.io/react-native/movies.json';

class WPReact extends Component {
  constructor(props) {
    super(props);

    this.state = {
      //Lets initialize results with the same struct we expect to receive from the api
      results: {
        title: '',
        description: '',
        movies: []
      }
    };
    //Using ES6 we need to bind methods to access 'this'
    this.fetchData = this.fetchData.bind(this);
  }

  componentDidMount() {
    this.fetchData();
  }

  fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          results: responseData
        });
      })
      .done();
  }

  render() {
    //this.state.results.movies is the array you have to iterate
    contents = this.state.results.movies.map((item) => {
      //We need to return the corresponding mapping for each item too.
      return (
          <View key={item.title} style={ styles.container }>
            <Text style={styles.title}>
              {item.title}
            </Text>
          </View>
        );
     });
    return (
      <View style={styles.container}>
        {contents}
      </View>
    );
  }
}

var Dimensions = require('Dimensions');

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#FFFFFF',
  },
  textContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 30,
    textAlign: 'center',
    margin: 10,
  },
  text: {
    fontSize: 18,
    paddingLeft: 20,
    paddingRight: 20,
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => WPReact);

Let me know if its works, I havent tested yet but I will soon.

Facundo La Rocca
  • 3,786
  • 2
  • 25
  • 47
  • The fetch is working without the bind. The issue is with the data that it is returning... – Pineda Mar 29 '17 at 13:22
  • Take a look at my second point. I think he needs to iterate results.movies instead – Facundo La Rocca Mar 29 '17 at 13:24
  • That second point is now moot, given the EDIT to the original question as a result of suggesting this exact point an hour ago :) – Pineda Mar 29 '17 at 13:25
  • Well, in the constructor, `results` is being initialized as an array. Why not trying to separate `results` in `movies` in one hand and rest of info in the other? – Facundo La Rocca Mar 29 '17 at 13:28
  • Depends on if the results returns one object or many. In the current case it returns a singular object, there is a possibility it was intended to return an array. This is something for the OP to clarify – Pineda Mar 29 '17 at 13:30
  • The fetch doesn't have to be bound, nor does the call to renderJSON. They are both within the scope of the ES6 class. – Pineda Mar 29 '17 at 13:35
  • I am for now trying to output a list of movie titles basically, this is a small experiment as part of a bigger project later - but it's giving me a few problems. – KT.C Mar 29 '17 at 13:39
  • I've created this playground for testing it: https://sketch.expo.io/Hk3IuVKne . I'm working on it to see what is going on – Facundo La Rocca Mar 29 '17 at 13:41
  • @FacundoLaRocca it appears to be working with what you posted. I just tried it, i had to remove the `return` in `return response.json()`, i was getting errors. I'm assuming my constructor was set up incorrectly and also discovered I didn't include `return` in `contents` – KT.C Mar 29 '17 at 14:00
  • oh Great!!! I've edited my answer to remove `return` from `return response.json()`, It was a copy/paste mistake. And The problem was probably in the constructor. Could accept my answer if it worked? Thanks. – Facundo La Rocca Mar 29 '17 at 14:04
  • @FacundoLaRocca thanks it helps me alot. can you help me here [Please-help](https://stackoverflow.com/questions/46137961/how-to-show-filtered-json-data-from-two-different-key-values-in-react-native) – Mayuresh Patil Sep 10 '17 at 06:40
0

React components using ES6 classes don't autobind this to non React methods. In your constructor, add:

this.renderJSON = this.renderJSON.bind(this)

Jérôme
  • 1,060
  • 1
  • 7
  • 18
0

responseData has to be an array for the Array#map() method to be available as a method on it.

You are setting results to an object containing the value of your responseData:

this.setState({ 
  results : { responseData } // Object literal notation albeit possibly incorrect depending on the value of responseData
});

Remove surrounding braces if you are sure responseData is an array:

this.setState({ 
  results :  JSON.parse(responseData);
  // You'll want to parse your JSON data here too :)
  // If responseData is an array, you'll be able to call .map on it
});

// this.state.results.movies.map should now work
// (given the structure of the expected JSON)
Pineda
  • 7,435
  • 3
  • 30
  • 45
  • so i have removed the braces, and has got me a little further, but still receive the same map error. – KT.C Mar 29 '17 at 12:47
  • Can you update your question to show the value returned in `responseData`? It could be that the value returned is not an array – Pineda Mar 29 '17 at 12:48
  • So this looks like you receive a single object. Are you expecting an array of these objects, or are you trying to map through the titles property? – Pineda Mar 29 '17 at 12:56
  • I've amended my answer to include the parsing of the JSON data when setting the results property on state – Pineda Mar 29 '17 at 13:06
  • when i add that, i get `JSON Parse error: Unexpected identifier "object"` – KT.C Mar 29 '17 at 13:24
  • Ahah, so it's likely that the code does not need to be parsed. It doesn't explain why your log for this.state.results.movies is undefined where are you calling this console log – Pineda Mar 29 '17 at 13:27