2

I read in this article that upon a state change, React schedules a re-render but doesn't execute it right away; rather it will kick it off at an optimal time. I am trying to understand if and how this might take place in the context of the current function execution, which has initiated the state change. If a function calls useState() but still has several lines of code left, I can see one of several possibilities happening:

  1. will it execute those lines first then kick off the re-render?
  2. will it do the re-render right away and then get back to executing the remainder of the function?
  3. will it re-render asynchronously at some point within the function execution that it deems most performant (and might be a different point each time)?
  4. will it re-render concurrently right away at the same time the function continues its execution?

To phrase my question in a different way, take this example code (trimmed for brevity):

const CreateButton = (props) => {
    const [processing, setProcessing] = useState(false);

    const create = () => {
        setProcessing(true);

        fetch("/api/test", {
          headers: {
            "Content-Type": "application/json",
          },
          method: "post",
          body: JSON.stringify(body),
        })
        .then(() => {
              setProcessing(false);
              console.log("We are not processing");
        });
    }

    return (
        <div>
          {processing ? (
            "Currently processing"
          ) : (
            <input type="button" onClick={create} />
          )}
        </div>
    );
};

We initialize the "processing" state value to false. Then in our return statement, we check whether this is true or false. Since its false, we call the "create" function, which sets processing=true. This is a change of state meaning a re-render gets scheduled. However, if you notice later in the create method we make an API request and in the subsequent .then() we set processing=false.

So, if the create() function finishes before the re-render it's possible it will just print "We are not processing" and call the create() method again. However, this re-render will have been initially invoked because we set processing=true, so we should expect it to instead print "Currently processing".

Alternately, if the re-render runs right away and then execution of the create() method continues, the flow would make sense here as it does what we would expect. This seems to be the outcome when I test this code but I want to make sure I'm fully understanding what React is doing under the hood.

user313
  • 681
  • 1
  • 8
  • 21
  • 3
    *"Since its false, we call the "create" function..."* No, that code never calls the `create` function. You **use** the create function as the value of the `onClick` property on the `input`, but that doesn't run it. It only runs if and when that button is clicked, not before. – T.J. Crowder Aug 24 '21 at 12:52
  • 1
    I've asked a sort of similar question previously and you may take a look at the answer to have a better sense of what it means by batching updates https://stackoverflow.com/a/55510736/9816472 – Isaac Aug 24 '21 at 12:54
  • @T.J.Crowder yes you are correct, I have done a poor job of summarizing that code. But back to my original question- if we click the button, it will call create(), initially set processing=true, and then go into a fetch API call, where is subsequently sets processing=false. Will the render happen before it sets processing=false? Is this a race condition I'd have to worry about? – user313 Aug 24 '21 at 12:59
  • 2
    A render can happen during the asynchronous `fetch` call, yes. For this code, that's not a race condition you'd need to worry about. – AKX Aug 24 '21 at 13:02
  • @Isaac thanks for linking that question, I am reading through it but still a bit unclear as to why the first code block (count + 1) doesn't update the state but the second (prevCount) does. The subsequent answer makes it seem using an updater function (e.g. updateState()) will always wait for the previous re-render to complete before modifying the state value- is my understanding on this correct? – user313 Aug 24 '21 at 13:03
  • 1
    yes you are right – Isaac Aug 24 '21 at 13:04
  • @AKX thank you. Just for my understanding, are you saying React will determine the best place to fit in the re-render among the async fetch call? It doesn't re-render concurrently while the fetch call is being made, correct? – user313 Aug 24 '21 at 13:05
  • 2
    Not only *can* a render happen while the ajax is running, but you *want* it to. :-) Because that's what hides the create button and shows the user you're doing something. But as @AKX said, it's not a problem in this code (more a good thing). Note that that isn't *during* `create`, it's during the ajax processing started by `fetch`. `create` returns almost immediately and isn't running anymore. – T.J. Crowder Aug 24 '21 at 13:23
  • A case where you might want to be mindful about races here is that if the `.then()` handler needs access to other state or props with the values at the time of completion, not at the time of declaration. Then you'd need the "latest ref" pattern, described in https://reactjs.org/docs/hooks-faq.html#what-can-i-do-if-my-effect-dependencies-change-too-often – AKX Aug 24 '21 at 13:42
  • @T.J.Crowder that makes a lot of sense, thank you for the explanation! – user313 Aug 24 '21 at 14:13

0 Answers0