0

I'm trying to set the state inside a then() of a Promise, but the state value is not getting saved and is not accessible outside of the then().

Below is the UPDATED code:

handleSelect = location => {
  this.setState({ location });
  geocodeByAddress(
    location
  )
  .then(results => getLatLng(results[0]))
  .then(latLng => {
    this.setState({
      location_latitude: latLng.lat,
      location_longitude: latLng.lng,
    })
    console.log(this.state.location_latitude); // returns the correct value
  })
  console.log(this.state.location_latitude); // returns null (the originally declared value)
}

If I console.log(this.state.location_latitude) I get a null value. But if I console.log inside the then() block, I get the correct value. How do I set the state in this case?

UPDATED explanation:

To give a better context to the whole logic here: I'm using a Google Places Autocomplete component which allows the user to select a location from the dropdown. The above method handleSelect is called when the user selects a location from the dropdown. I then extract the latitude and longitude of the place selected by the user using the geocodeByAddress method, which is a promise. I need to then retrieve the latitude and longitude and set the state accordingly, which is later used to send to the database. I can't submit the value to the database yet because there are other form inputs that the user needs to fill (which are also stored in the state) and once he clicks on Submit, I'll be submitting all the state elements in a single call to the backend. So, there is nothing I want to update with componentDidMount() or nothing is getting updated yet to use componentDidUpdate(). In any case, I can only store the state value inside the then of geocodeByAddress (please correct me if I'm wrong here). So, I HAVE to be able to modify the state value inside the then block.

ashishsanjayrao
  • 101
  • 2
  • 14
  • where is your `console.log(location_latitude)` that logs null? – Nicholas Tower May 31 '19 at 18:12
  • Where is the rest of the code around this function call? need the code for the whole react class method. Somewhere you're losing the `this` context – John Ruddell May 31 '19 at 18:12
  • If you lost `this` context, don't forget to bind your function to `this` in the constructor. –  May 31 '19 at 18:13
  • If you are trying to access this state to get it's value and use somewhere first, you should use `ComponentDidMount`. So that the value is present in the state when you access it. – Pedro Vieira May 31 '19 at 18:16
  • Possible duplicate of [this.setState is not a function](https://stackoverflow.com/questions/33381756/this-setstate-is-not-a-function) – John Ruddell May 31 '19 at 18:17
  • Not sure why everyone's assuming it's an issue with `this`. There's no mention of any exceptions being thrown, and everything in the code provided is done using arrow functions. – Nicholas Tower May 31 '19 at 18:20
  • @NicholasTower there isn't enough information provided to know what the issue is. I suspect the context isn't bound correctly considering the `setState` is not applying. Part of the missing info could be an exception is thrown `setState is not a function` or whatever the error is. But regardless need more info to know what the issue is :) – John Ruddell May 31 '19 at 18:34
  • Agree we need more info. My suspicion is they're just putting their logging in a place where the setState hasn't completed yet. – Nicholas Tower May 31 '19 at 18:37
  • "If I console.log(location_latitude) I get a null value" and what if you `console.log(this.state.location_latitude)`? – Robbie Milejczak May 31 '19 at 20:14
  • Hey guys, apologies for the lack of information and context. I've made edits to the post. Hope this helps. Please let me know if you would like me to share more of the code. – ashishsanjayrao Jun 01 '19 at 07:04
  • I'm not sure I'm losing the context of `this` because I'm able to access the value inside the `then` block. There are no exceptions or errors, as well. – ashishsanjayrao Jun 01 '19 at 07:06
  • @RobbieMilejczak - I'm sorry I meant to write `console.log(this.state.location_latitude)`. I've updated the post as well. Thanks. – ashishsanjayrao Jun 01 '19 at 07:07
  • @ashishsanjayrao based on your updated code it is working exactly as it should, the `console` call outside of your promise is always going to be evaluated before your promise completes this is just the nature of asynchronous code. I'd suggest checking out [`async / await syntax`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function#Rewriting_a_promise_chain_with_an_async_function) it will help you avoid these kinds of issues, – Robbie Milejczak Jun 01 '19 at 13:57
  • setState was actually working, but not instantly. I didn't quite know about this fact. I was able to get retrieve the value later in the code. Thanks a lot for all your responses! – ashishsanjayrao Jun 01 '19 at 17:44

3 Answers3

2

The thing is when you set-state you can't see the state result immediately after that. That's why the console.log returns null. Make sure your console.log is in the render block. This is because set-state actions are asynchronous and are batched for performance gains. This is explained in documentation of setState. Take a look at this => Why is setState in reactjs Async instead of Sync?

Afsanefda
  • 3,069
  • 6
  • 36
  • 76
  • Thanks! Yes, I did realise that the value is actually getting updated, just not instantly. I was able to get this resolved. Thank you! – ashishsanjayrao Jun 01 '19 at 17:42
0

You need to bind the function context to current class/ component. Try something like below. Create a function like "callSetState" bind it with this and while calling in then just pass the values as parameters to it.

import React, { Component } from 'react';
class Test extends Component{
    constructor() {
        super()
        this.callSetState = this.callSetState.bind(this);
    }

    callSetState(latitude, longitude) { 
        this.setState({
            location_latitude: latitude,
            location_longitude: longitude,
        })
    }

/* Call this func in your class and call callSetState which is already bound to your component in constructor */

    // geocodeByAddress(location)
    //     .then(results => getLatLng(results[0]))
    //     .then(latLng => this.callSetState(latLng.lat, latLng.lng));

}
Atul
  • 3,013
  • 2
  • 12
  • 15
-1

This is clearly an issue with your data call not react.

Your react code looks fine but I suggest spitting the code.

1) service.js file - this does your query and returns data or returns error.

2) location component, componentDidMount() or useEffect(() => {}, []) gets the data and updates state/useState()

To give you a code snippet I need to you to provide a console log of latLang & results.

Many thanks

Neil
  • 971
  • 2
  • 12
  • 33