Erik Putz Erik Putz - 1 year ago 139
React JSX Question

In react native this.setState() does not changes the state in fetch function

I have been searching for solutions for day, I learned a lot, but did not figure out what is wrong. Here what I do:

  1. Calling the App constructor; initialising the state loading, dataSource and data

  2. when the component mounts, the program calls the getData function with the requested URL

  3. The getData function is an asynchronous fetch function

  4. then the data is converted to JSON

  5. then the JSON is cloned to became a datablob for the webview

  6. then the setState function is called, changing the loading and the data.

This is where the setState does not fire. Not even the the render (it should). Every tutorial, every forum shows it to be this way (and its also logical).

Here is the code:

import Exponent from 'exponent';
import React from 'react';
import {
} from 'react-native';

import { Button, Card } from 'react-native-material-design';
import {UnitMenuCard} from './unitmenucard.js';

class App extends React.Component {
constructor(props) {
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
loading: true,
dataSource: ds,
data: null

componentDidMount() {

getData(url) {
console.log('loading data');
return fetch(url).then(
(rawData) => {
console.log('parsing data');
return rawData.json();
(jsonData) =>
console.log('parsing to datablobs');
let datablobs = this.state.dataSource.cloneWithRows(jsonData);
console.log('datablobs: ' + datablobs);
return datablobs;
(datablobs) => {
console.log('setting state');
this.setState = ({
loading: false,
data: datablobs
console.log('the loading is ' + this.state.loading);
console.log('the data is ' +;
).catch((errormsg) =>
{console.error('Loading error: ' + errormsg);}

render() {
console.log('loading is ' + this.state.loading);
var dataToDisplay = '';
if(this.state.loading) {
dataToDisplay = <ActivityIndicator animated={true} size='large' />
} else {
//let jdt = this.state.dataSource.cloneWithRows(;
dataToDisplay = <ListView
renderRow={(unit) => <UnitMenuCard name={} image={unit.picture} menu={}/>}
return (
<View style={styles.container}>

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',



Did I missed something? Thank you forward for your answer mighty Stack Overflow.

Answer Source

I think you misunderstood how DatSource works.

getInitialState: function() {
  var ds = new ListViewDataSource({rowHasChanged: this._rowHasChanged});
  return {ds};
_onDataArrived(newData) {
  this._data = this._data.concat(newData);
     ds: this.state.ds.cloneWithRows(this._data)

This is taken from the docs here:

See how you need to clone your datasource object in the state. The key thing is that you call cloneWithRows passing the data you got back from the API (in your case jsonData). This creates a cloned datasource, containing the data you just fetched.

In your code instead you just create a clone of your data source, but never replace the actual one you list view is linked to. You should do it this way:

    (datablobs) => {
      console.log('setting state');
      this.setState = ({
        loading: false,
        dataSource: datablobs
      console.log('the loading is '  + this.state.loading);
      console.log('the data is '  +;

You don't need for this, the list view reads from the datasource object. You can also avoid having two .then calls by simply doing everything in one.

You also have another problem. You have the list view linked to the wrong property in the state. You list view code in render should be:

dataToDisplay = <ListView
    renderRow={(unit) => <UnitMenuCard name={} image={unit.picture}   menu={}/>}

this.state.dataSource is where you store the data source object.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download