Colin
Colin

Reputation: 2487

Call this.setState() in React Native from Inside Callback Function

I am new to React Native and am trying to parse XML into a FlatList and then save it to the State object. In order to parse the XML, I am using react-native-xml2js and the simple function, parseString. The parseString has a callback function and I am trying to use the setState function within that callback function. However, "this" is undefined in the callback and, when I try to pass "this" as a different variable (in this case, "self"), it's still undefined. What am I doing wrong here?

export default class PostList extends Component<{}> 
{

   constructor(props) {
      super(props);
      this._executeQuery('https://website.com/xml-feed/');
      this.state = {
        resultsLoaded: false,
        results: false,
      };
    };

  _keyExtractor = (item, index) => index;
  _renderItem = ({item, index}) => (
      <ListItem
        item={item}
        index={index}
        onPressItem={this._onPressItem}
      />
    );
    _renderList = (response) => {
        return <FlatList
        data={response.rss.channel[0].item}
        keyExtractor={this._keyExtractor}
        renderItem={this._renderItem}
      />;
    };
    _handleResponse = (response) => {
        var parseString = require('react-native-xml2js').parseString;
        var self = this;
        this.setState({ resultsLoaded: true });
        parseString(response, function (err, result, self) {
            console.log(self);
            self.setState({ resultsLoaded: true, results: self._renderList(result) });
        });
    };
    _executeQuery = (query) => {
      fetch(query)
          .then(response => response.text())
          .then(str => this._handleResponse(str))
          .catch(error =>
             console.log('ERROR: '+error)
         );
    };

  render() {
    const results = this.state.resultsLoaded ?
        this.state.results : null;
    const loading = !this.state.resultsLoaded ?
            (<View><Text style={styles.description}>
          Loading Blog Posts
        </Text>
        <ActivityIndicator size='large'/></View>) : null;
            console.log(this.state.resultsLoaded);
    return (
      <View style={styles.container}>
        {loading}
        {results}
      </View>
    );
  };

}

Upvotes: 1

Views: 1180

Answers (2)

Sarantis Tofas
Sarantis Tofas

Reputation: 5167

Remove self parameter from the callback function:

_handleResponse = (response) => {
    var parseString = require('react-native-xml2js').parseString;
    var self = this;
    this.setState({ resultsLoaded: true });
    parseString(response, function (err, result) {
        console.log(self);
        self.setState({ resultsLoaded: true, results: self._renderList(result) });
    });
};

Having self as a parameter will always return undefined

Another option would be to bind this with the callback function:

parseString(response, function (err, result) {
  console.log(this);
  self.setState({ resultsLoaded: true, results: self._renderList(result) });
}.bind(this));

And finally you could make use of ES6 Arrow functions:

parseString(response, (err, result) => {
  console.log(this);
  self.setState({ resultsLoaded: true, results: self._renderList(result) });
});

Upvotes: 2

Val
Val

Reputation: 22797

You'll have access to this by removing the arrow, declare function directly:

_handleResponse(response) {
    var parseString = require('react-native-xml2js').parseString;
    var self = this;
    this.setState({ resultsLoaded: true });
    parseString(response, function (err, result, self) {
        console.log(self);
        self.setState({ resultsLoaded: true, results: self._renderList(result) });
    });
};

There's a detail answer about Arrow Function and this.

Upvotes: 0

Related Questions