user3802348
user3802348

Reputation: 2592

React-native prevent functions from executing asynchronously?

Upon clicking a button in my react Native app, I call two functions: getSolutionListFromDatabase, which sets the state of the component to include this solution list, and then updateDatabaseSolutionList, which adds an element to this list and pushes it back to Firebase. However, although the state of the application is being properly updated within the first function, within the second function the state is being logged as undefined, and my log statements for that function are being called before some statements in the first function. Are the functions operating asynchronously for some reason, and is this a feature of React native? If so, how can I prevent the second function from executing until the state has been set? Thanks.

onSubmitPressed: function() {
    if (this.checkSolution) {
        this.getSolutionListFromDatabase();
        this.updateDatabaseSolutionList();
        Alert.alert(
            'Correct!',
            "Woohoo!"
        );
    }
},

getSolutionListFromDatabase: function() {
    var thisItemRef = itemsListRef.child(this.state.itemID);
    thisItemRef.once('value', (snap) => {
        var solutionList = snap.val();
        this.setState({
            solutionList: solutionList
        });
        console.log('solution is set as' + this.state.solutionList);
    });
},

updateDatabaseSolutionList: function() {
    var newSolutionList = [];
    console.log('solutionList undefined here' + this.state.solutionList);

    if (this.state.solutionList) {
        newSolutionList = this.state.solutionList;
        newSolutionList.push(this.props.itemID);
    }

    //then push new list to Firebase
},

Upvotes: 0

Views: 661

Answers (2)

Frank van Puffelen
Frank van Puffelen

Reputation: 598837

The logic will always be the same as in the answer to your previous question. If you have a dependency between events, you should move the invocation into the first callback:

onSubmitPressed: function() {
    if (this.checkSolution) {
        this.getSolutionListFromDatabase();
    }
},

getSolutionListFromDatabase: function() {
    var thisItemRef = itemsListRef.child(this.state.itemID);
    thisItemRef.once('value', (snap) => {
        var solutionList = snap.val();
        this.setState({
            solutionList: solutionList
        });
        console.log('solution is set as' + this.state.solutionList);
        this.updateDatabaseSolutionList();
    });
},

updateDatabaseSolutionList: function() {
    var newSolutionList = [];
    console.log('solutionList undefined here' + this.state.solutionList);

    if (this.state.solutionList) {
        newSolutionList = this.state.solutionList;
        newSolutionList.push(this.props.itemID);
    }

    Alert.alert(
        'Correct!',
        "Woohoo!"
    );
    //then push new list to Firebase
},

This type of flow becomes a lot easier to follow for yourself if you pass the prerequisite (i.e. solutionList) into the function that requires it, instead of using a field/property:

onSubmitPressed: function() {
    if (this.checkSolution) {
        this.getSolutionListFromDatabase();
    }
},

getSolutionListFromDatabase: function() {
    var thisItemRef = itemsListRef.child(this.state.itemID);
    thisItemRef.once('value', (snap) => {
        var solutionList = snap.val();
        this.updateDatabaseSolutionList(solutionList);
    });
},

updateDatabaseSolutionList: function(solutionList) {
    solutionList.push(this.props.itemID);

    Alert.alert(
        'Correct!',
        "Woohoo!"
    );
    //then push new list to Firebase
},

But even better would simply be to push() the new value straight to Firebase, instead of first downloading the entire array and then sending it back with a single new item added to it:

onSubmitPressed: function() {
    if (this.checkSolution) {
        itemsListRef.child(this.state.itemID).push(...);
    }
},

Upvotes: 5

Austin Greco
Austin Greco

Reputation: 33544

setState is not guaranteed to be synchronous, so you can't rely on the state being updated right away after it.

See https://facebook.github.io/react/docs/component-api.html

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

The API does provide a callback for when the state is actually updated though:

void setState(
  function|object nextState,
  [function callback]
)

Alternatively, you could pass the solutionList directly to the next function, and then set them both in the state at the same time, which seems like the better option.

Upvotes: 2

Related Questions