skip to Main Content

I have a react onChange method on a dropdown UI component (Custom react component, not standard HTML) which updates the state to the newly selected value when a user selects an item from the dropdown, then makes a callback to retrieve new data from an API based on this new state.

Despite the API call being in a callback, the state is always stale by 1 interaction, meaning if I select 1, it will make the API call with the relevant state variable set to 0 (default value), and if I then select 2, it makes the API call with the relevant state variable set to 1.

Usually this happens because the function being called is NOT a callback, but in this case it is so I am confused how the state remains stale.

I know there are not other functions firing as there are no ComponentDidUpdate calls or anything else to interfere with this process.

I have confirmed with console logging that the new value that the state is supposed to be set to is correct in the onChange function prior to setting state, and yet the state is not set to the new value in the callback function.

I didn’t think this was possible and am stumped on what to try next to troubleshoot this, so any ideas on what this might be caused by are appreciated.

Edit: Here is code (Names genericized, some details excluded such as permission checking code etc.)

When this code executes due to the callback function being called, apiFieldValue is always stale as described, despite being in the callback from the onChange function. I cannot share significantly more than that since it is code that I do not have rights to distribute and as such cannot supply.

onChangeFunction (newValue) 
{
    this.setState({ stateValue: newValue}, this.ApiCallback())
}

ApiCallback (){
   var canMakeCall = this.state.hasPermissions,
       apiFieldValue = this.state.stateValue

   if (canMakeCall){
    let response = genericHttpClient.post("apiurl.com", {apiField: apiFieldValue, credentials: this.state.userCredentials})
...
}

}

2

Answers


  1. The state does not update before you make the api call. Simply pass in the newValue instead of the state as shown below when you make the call.

    The other option is to use a useEffect() hook. It sounds like you are using class based components so don’t worry about this if it is confusing. I’ve included the code for both options anyways.

    // By passing a value to the callback
    onChangeFunction(newValue) {
      this.setState({
        stateValue: newValue
      })
      ApiCallback(newValue)
    }
    
    ApiCallback(value) {
      var canMakeCall = this.state.hasPermissions,
        apiFieldValue = value
    
      if (canMakeCall) {
        ...
      }
    }
    
    
    // With useEffect() hook
    onChangeFunction(newValue) {
      this.setState(prev => {...prev, valueKey: newValue})
    }
    
    useEffect(() => {
      ApiCallback();
    }, [state.valueKey])
    
    ApiCallback() {
      var canMakeCall = state.hasPermissions,
        apiFieldValue = state.valueKey
    
      if (canMakeCall) {
        ...
      }
    }
    Login or Signup to reply.
  2. See this pitfall in the React docs, as well as the Caveats down below it.

    In short, the state hasn’t yet updated when you call your this.ApiCallback().

    There are various workarounds, but if you want to use the callback parameter on this.setState, you would need to give it a function so it gets called later:

    this.setState({ stateValue: newValue}, () => this.ApiCallback())
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search