rnn
rnn

Reputation: 2563

TypeError: Undefined is not a function (evaluating 'this.state...map')

**In this my code, I got an error which says 'this.state.leader.map' is not a function.

I searched on web about my problem but none of them solved my issue.

This is my code which is below. I hope, code is clear to understand easly**

import React, { Component } from 'react';
import { View, Text, FlatList } from 'react-native';
import DOMParser from 'react-native-html-parser';
import axios from 'axios';

export default class header extends Component {

    state = { leader: [] };

    componentWillMount() {
        fetch('url')
        .then(response => {
            if (response.ok) {
                return response;
            }else {
                let error = new Error('Error ');
                error.response = response;
                throw error;
            }
            },
            error => {
                let errmess = new Error(error.message);
                throw errmess;
            })
        .then(response => response.text())
        .then(leaders => {
            const str = leaders.substring(76);
            const str2 = str.substring(0, str.length - 9);

            this.setState({ leader: str2 });
        })
        .catch(error => {
            this.setState({ errMessage: error.message });
        });
        }

        renderall() {
            return this.state.leader.map(alb => <Text>{alb.Ref}</Text>  
        }

    render() {

        console.log(this.state.leader);

        return (
            <View>
              {this.renderall()}
            </View>
        );
    }
}

Upvotes: 2

Views: 1884

Answers (4)

Andrew
Andrew

Reputation: 28539

You issue is with the fact that you are setting a string to the value of leader in your state.

If we look at your code you are taking a substring of leaders. To do this leaders must be a string. You are then saving that string to your leader value in state.

 .then(leaders => {
    const str = leaders.substring(76);
    const str2 = str.substring(0, str.length - 9); // <- this is a string

    this.setState({ leader: str2 }); 
  })

In your state you are setting leaders in the following way

state = {
  leaders: [] // <- this is an array
}

Individually doing these things are fine, but there is a disconnect between what you are doing in your fetch request and what you then want to do in your renderAll method as you cannot use .map on a string.

renderall() {
   return this.state.leader.map(alb => <Text>{alb.Ref}</Text>   
  // .map requires this.state.leader to be an array but you 
  // have now changed it to a string so it won't work
}

Either you need to change what you are storing in leader in your fetch request so that it is an array. Or you need to change what is happening in your renderAll function.

XML parsing

If your XML is the same as you put in your comment

<?xml version="1.0" encoding="utf-8"?> 
<string xmlns="url">[{"Ref":"IHR1900299","Unvan":"max"},{"Ref":"IHR1900298","Unvan":"max2"}] </string>

Then you could use a simple regex to capture what you want and then use JSON.parse to convert it into json.

Once you have your xml as a string, you could do this:

// I'm hardcoding it as a string, because I don't have your api to call.
let xmlString = '<?xml version="1.0" encoding="utf-8"?> <string xmlns="url">[{"Ref":"IHR1900299","Unvan":"max"},{"Ref":"IHR1900298","Unvan":"max2"}] </string>';

let jsonString = xmlString.match(/\[.*\]/);
let json = JSON.parse(jsonString);
    
console.log(json)
console.log(json[0].Ref)

Upvotes: 1

dentemm
dentemm

Reputation: 6379

Your renderAll method needs to return a single JSX element in order to be correct. Additionally I reformatted the method to use arrow functions to avoid all those nasty binding issues. Also: you need to provide a unique key to each of the items.

renderall = () => {
  return (
    <View>
     {this.state.leader.map(alb => {
       return <Text key={alb.Ref}>{alb.Ref}</Text>;
     })}
   </View> 
}

Upvotes: 0

Saeid
Saeid

Reputation: 2046

This happen because the fetch function is asynchronous. So, all you need is to add a simple condition and passing this:

renderall() {
            return this.state.leader.map(alb => <Text>{alb.Ref}</Text>  
        }

 render() {

        console.log(this.state.leader);

        return (
            <View>
              {
                (this.state.leader?this.state.leader.length>0:false)&&
                this.renderall(this)
              }
            </View>
        );
    }

Upvotes: 0

Nino Filiu
Nino Filiu

Reputation: 18561

React component usual methods (like componentWillMount or render) are automatically bound to this by React, contrary to your custom method renderAll. Add the following to your class:

constructor(props) {
  super(props);
  this.renderAll = this.renderAll.bind(this);
}

Useful links:

Upvotes: 1

Related Questions