0

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
},

2 Answers 2

5

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(...);
    }
},
Sign up to request clarification or add additional context in comments.

Comments

2

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.

1 Comment

Thanks for the tips. When I try to pass the solutionList directly to the next function by just returning snap.val(), the function returns as undefined, even though it is logging the correct values for snap.val within the function. Any ideas what could be happening here?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.