Second useState doesn't update in promise chain

85 views Asked by At

With React Native, this app needs to:

  1. Move an object
  2. Then update text (a number) on the screen
  3. Then move another object
  4. Then update the same text (another number) on the screen

Problem: #4 does not update on the screen.

This GIT link shows the current code.

Google explains the issue: "The useState hook in React does not update twice in a promise chain. This is because React batches state updates, meaning that all state updates that occur within a single event loop cycle are grouped together and applied at the end of the cycle. This helps to improve performance by avoiding unnecessary re-renders."

So maybe this button should call two Promises one after the other, with each one chained to a useState update. The second promise animation could have a delay of the same duration as the first animation, to make them consecutive. I haven't plugged this in yet to try it, will do so soon.

The animating functions look like this:

  function updateNumCatsMoved(){
    let newNumCats = { ... numCatsMoved };
    newNumCats.value = newNumCats.value + 1;
    setNumCatsMoved(newNumCats);
    console.log("numCatsMoved:",numCatsMoved.value);
  };

  async function pressablePromise(){
    new Promise(function(myResolve, myReject) {
      cat1X.value = withTiming(cat1XEnd, config);
      cat1Y.value = withTiming(cat1YEnd, config, () => {
        runOnJS(myResolve)(); 
      }); 
    })

    .then(function() {  return updateNumCatsMoved();})

    .then(
      function() { 
        cat2X.value = withTiming(cat2XEnd, config);
        cat2Y.value = withTiming(cat2YEnd, config, () => {
          return; 
        }); 
      }
    )
     // doesn't update on the screen 
    .then(function() {  return updateNumCatsMoved();})  
  }
2

There are 2 answers

0
RNAppCoder On BEST ANSWER

This was achieved by passing a value to the useState function. The value is the amount to update with. In my real app, the update can be -1, 0, or +1. This problem led to me learning about promises and how React batches state updates.

A bit of an issue to deal with moving forward is that the useState starts with 0 for both updates. I think I can use a local variable to help with that.

And so it WAS doing both updates, but they each started at 0. So it only went from 0 to 1.

  function updateNumCatsMoved(n){
    let newNumCats = { ... numCatsMoved };
    newNumCats.value = newNumCats.value + n;
    setNumCatsMoved(newNumCats);
  };

  function pressablePromise(){
    new Promise(function(myResolve, myReject) {
      cat1X.value = withTiming(cat1XEnd, config);
      cat1Y.value = withTiming(cat1YEnd, config, () => {
        runOnJS(myResolve)(); 
      }); 
    })

    .then(function() {  return updateNumCatsMoved(1);})

    .then(
      function() {
        return new Promise((resolve, reject)=>{
          cat2X.value = withTiming(cat2XEnd, config);
          cat2Y.value = withTiming(cat2YEnd, config, () => {
            runOnJS(resolve)();
          }); 
        })
      }
    )
     
    .then(function() {  return updateNumCatsMoved(2);})  
  }
1
Jonas Beck On

withTiming callback function seems to be an async function.

So you need to update

.then(
      function() { 
        cat2X.value = withTiming(cat2XEnd, config);
        cat2Y.value = withTiming(cat2YEnd, config, () => {
          return; 
        }); 
      }
    )

to

.then(
      function() {
        return new Promise((resolve, reject)=>{
          cat2X.value = withTiming(cat2XEnd, config);
          cat2Y.value = withTiming(cat2YEnd, config, () => {
            resolve(); 
          }); 
        })
      }
    )