javascriptreact-nativepromisecallbackreact-native-reanimated

Second useState doesn't update in promise chain


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();})  
  }

Solution

  • 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);})  
      }