waze
waze

Reputation: 527

React Native fetch only returns on second or third attempt

I have a fetch that uses a parameter that is saved locally with AsyncStorage. But my fetch only returns data on second or third attempt, so when I try to map the data on my render it says it cannot map undefined data.

Here is my AsyncStorage and fetch code:

  componentWillMount(){
    AsyncStorage.getItem('key').then((codigo)=>{
      this.setState({value: JSON.parse(codigo)});
      this.getData()
    })
  }


  getData(){
    fetch(`URL/portalacv_ws.asmx/GetDetalhesViatura?CarID=${this.state.value}`)
    .then((response) => { return response.json()})
    .then(res => {
       this.setState({data: res})
    })
  }

this is what I get on console:

enter image description here

Upvotes: 1

Views: 1473

Answers (1)

Facundo La Rocca
Facundo La Rocca

Reputation: 3866

The problem you are facing is both method are async. In your case you should call to getData as a callback after getting the item.

Explanation with your code:

componentWillMount(){
  AsyncStorage.getItem('key').then((codigo)=>{
    //This code runs async, so when you call getData, value has not been changed yet (Or at least you cannot be sure).
    this.setState({value: JSON.parse(codigo)});
    //Printing here this.state.value will help you to understand
    this.getData()
  })
}

getData(){
  fetch(`URL/portalacv_ws.asmx/GetDetalhesViatura?CarID=${this.state.value}`)
  .then((response) => { return response.json()})
  .then(res => {
     this.setState({data: res})
  })
}

How to solve it?:

componentWillMount(){
  AsyncStorage.getItem('key').then((codigo)=>{
    this.setState({value: JSON.parse(codigo)}, () => {
        //Here you are pretty sure that the setState has already done.
        this.getData()
    });
  })
}

getData(){
  fetch(`URL/portalacv_ws.asmx/GetDetalhesViatura?CarID=${this.state.value}`)
  .then((response) => { return response.json()})
  .then(res => {
     this.setState({data: res})
  })
}

EDITED:

After taking a look at the whole component, the conclusion is that render method is being executed once before setState and once after, that is why you have the first time undefined, and the second time the value you are expecting.

So one possible way in order to solve this situation, is by flaging the action of fetching data and render after the fetch is finished. More or less, the idea would be:

export default class Favoritos extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: null,
      data: null,
      fetching: false
    };

    //Binding is not needed, but...
    this.getData = this.getData.bind(this);
    this.onPress = this.onPress.bind(this);
  }

  componentWillMount(){
    this.setState({ fetching: true }, () => {
      AsyncStorage.getItem('key').then((codigo)=>{
        this.setState({value: JSON.parse(codigo)}, () => {
          this.getData()
            .then((data) => {
              this.setState({
                data: data,
                fetching: false
              })
            })
        });
      })
    });
  }


  getData(){
    return fetch(`URL/portalacv_ws.asmx/GetDetalhesViatura?CarID=${this.state.value}`)
             .then((response) => { return response.json()})
  }

  onPress(){
    this.setState({ fetching: true }, () => {
      this.getData()
        .then((data) => {
          this.setState({
            data: data,
            fetching: false
          })
        })
    });
  }   

  render() {
    if(this.state.fethcing){
      return (
        <View style={{ flex: 1, backgroundColor: 'white' }}>
          Fetching data...
        </View>
      );
    } else {
      return (
        <View style={{ flex: 1, backgroundColor: 'white' }}>
          <ScrollView>
            <TouchableHighlight onPress={this.onPress}>
              ...
            </TouchableHighlight>
            <Text>
              {this.state.value}
            </Text>
          </ScrollView>
        </View>
      );
    }
  }
}

In the code above, I've left only the code that makes sense to question, the original one has much more code.

Upvotes: 2

Related Questions