简体   繁体   中英

Redux store doesn't get updated synchronously?

I noticed something in my code and i just wanted to confirm it here. (using Redux and React)

Let's say i have a button and when you click on it, this function gets called:

onClick={ () => {
updateSomeVar(10);
console.log(someVar);
}}

Where updateValue() dispatches an action that updates the value of someVar in the store to be 10. However, in the following console.log , it still prints the old value of someVar , instead of 10 .

Is it because the console.log runs before the store update triggers a React component re-render and that's why we still see the old value? (after the component re-renders, the value is indeed 10 ).

I just thought Redux actions are synchronous, so i expected the console.log to have the most recent value?

Yes, the state updates are synchronous by default unless you are using a middleware like thunk .

What's happening here is that, you are logging the value after the call to updateSomeVar which re-renders the component.

Each render has its own set of values. Observe this behaviour by using a setTimeout to the console.log . It will still print the older value.

This is because state values are used by the component based on the current closure and therefore, the state values will be reflected when the closure is updated (which happens when the component re-renders).

Look at it this way, here is a very simplified implementation of React hooks and state updates.

  let React = (function() {
  let global = {}; // define a global variable where we store information about the component
  let index = 0; // index to keep track of the component's state
  function render(Component) {
    global.Component = Component;
    const instance = Component(); // get the instance of the component
    index = 0;
    instance.render();  // call the component's render function
    
    global.instance = instance; // store the component's instance for any future calls of the component's functions
    return global; // return the global variable
  }

 function useState(initialState) {
    if (!global) {
      throw new Error("Need a global");
    }

    if (!global.hooks) {
      global.hooks = []; // this array holds the state of the component
    }

    const hooks = global.hooks;
    const currentState = global.hooks[index] || initialState; 
    hooks[index] = currentState;    // memoize the state for future access
   
    const setState = (function() {
      let currentIndex = index; // copy the index so each useState call will have it's own "closed" value over index (currentIndex)
      return function(value) {
        global.hooks[currentIndex] = value;
        render(global.Component);   //re-render the component after state change
      };
    })();
    index = index + 1;
    return [currentState, setState];
  }

  return { render, useState, useEffect };

})();

function Component() {

  // Component is called at each re-render. index is reset to 0.
  
  const [count, setCount] = React.useState(0);
  // hooks: [0], currentIndex: 0,  Incremented Index: 1
  
  const [word, setWord] = React.useState("");
  // hooks: [0, ''], currentIndex: 1,  Incremented Index: 2
  
  const countSetter = () => {
    setCount(count + 1);
  };

  const wordSetter = word => {
    setWord(word);
  };

  function render() {
    console.log(`Count is: ${count}, Word is: ${word}`);
  }
  
  return { render, countSetter, wordSetter };
}

const global = React.render(Component);     // hooks: [ 0, '', [ 0, '' ], [] ]
global.instance.countSetter();              // hooks: [ 1, '', [ 1, '' ], [] ]
global.instance.countSetter();              // hooks: hooks: [ 2, '', [ 2, '' ], [] ]
global.instance.countSetter();              // hooks: [ 3, '', [ 3, '' ], [] ]
global.instance.wordSetter("yooo");         // hooks: [ 3, 'yooo', [ 3, 'yooo' ], [] ]
global.instance.wordSetter("ssup");         // hooks: [ 3, 'yooo', [ 3, 'yooo' ], [] ] 

Your console.log closes over the value of the state variable for that current render therefore, it returns the value owned by that particular render.

By going over the above implementation, when the component re-renders, the index is updated and then and only then, the new value will be available to the component.

Here's the link to the implementation explaining the concept:

https://rohitpotato.hashnode.dev/how-react-implements-usestate-and-useeffect-internally-a-simplified-overview-1

Github link: https://github.com/rohitpotato/implement-react-hooks

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM