[详 细 解 析] [详 细 解 析] [详 细 解 析] [详 细 解 析] [详 细 解 析] [详 细 解 析]

Writing in the front

Recently in learning React18 relevant updates, also can saying is a whim want to translate some thing, also is for their own learning, also want to learn together with the community students, first time translation, semantics may be different, there are a lot of students if there are any wrong place, you can refer to the original, also expect students to point out that this paper is not the fact.

An overview of the

React 18 adds out-of-the-box performance improvements by doing more batching by default, removing manual batch updates in application or library code. This article will explain what batch processing is, how it used to work, and what has changed.

Note: This is an underlying new feature (new change) that we don’t want to make many users think. However, it may be relevant to educators and library developers.

What is batch processing

Batch processing is the merging of a set of status updates into a single re-render for better performance.

For example, if you have two states that need to be updated in a click event, React will always put them in a re-render. If you click on the code below, you’ll notice that React only rerenders once every time you click, even though you updated the state twice.

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    setCount(c= > c + 1); // Do not rerender for the time being
    setFlag(f= >! f);// Do not rerender for the time being
    // React will only be rerendered one last time
    // React will only re-render once at the end (that's batching!)
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black}} ">{count}</h1>
    </div>
  );
}

Copy the code

React 17 Batches Inside Event Handlers

This is a great performance boost because it avoids a lot of unnecessary rerendering. It can also prevent your component from showing a half-finished state due to a single state update, and some may even cause problems. This might remind you that the server doesn’t go to the kitchen to place your order until after you’ve finished, not right after you order your first dish.

But react doesn’t behave consistently in batch processing. For example, if you take online data and update the status in a callback, React doesn’t batch those updates, but instead makes separate updates.

This is because React used to do batch processing only at browser time (click, for example), but we update the state in the callback that handles the event

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    fetchSomething().then(() = > {
      // React 17 and earlier does NOT batch these because
      // they run *after* the event in a callback, not *during* it
      setCount(c= > c + 1); // Causes a re-render
      setFlag(f= >! f);// Causes a re-render
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black}} ">{count}</h1>
    </div>
  );
}

Copy the code

React 17 does NOT batch outside event handlers.

Until Act18, we only did batch processing in React event handling. We will not do batch processing in the Promise setTimeout Native Event Handle.

What is automatic batching

Focusing on Act18, all updates are automatically batched, no matter where they come from.

This means that promise native Event Handles, or any other Event, will be batched like React events. We expect this to result in less rerendering and better performance in your application.

function App() {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);

  function handleClick() {
    fetchSomething().then(() = > {
      // React 18 and later DOES batch these:
      setCount(c= > c + 1);
      setFlag(f= >! f);// React will only re-render once at the end (that's batching!)
    });
  }

  return (
    <div>
      <button onClick={handleClick}>Next</button>
      <h1 style={{ color: flag ? "blue" : "black}} ">{count}</h1>
    </div>
  );
}

Copy the code
  • ✅ Demo: React 18 with createRoot batches even outside event handlers! (Notice one render per click in the console!)

  • 🟡 Demo: React 18 with legacy render keeps the old behavior (Notice two renders per click in the console.)

React automatically batches updates regardless of where they come from, as in the following example

function handleClick() {
  setCount(c= > c + 1);
  setFlag(f= >! f);// React will only re-render once at the end (that's batching!)
}
Copy the code

The following example shows the same

setTimeout(() = > {
  setCount(c= > c + 1);
  setFlag(f= >! f);// React will only re-render once at the end (that's batching!)
}, 1000);
Copy the code
fetch(/ *... * /).then(() = > {
  setCount(c= > c + 1);
  setFlag(f= >! f);// React will only re-render once at the end (that's batching!)
})
Copy the code
elm.addEventListener('click'.() = > {
  setCount(c= > c + 1);
  setFlag(f= >! f);// React will only re-render once at the end (that's batching!)
});
Copy the code

Note: React ensures that each batch is secure. React ensures that for each user-initiated event, such as a click or keystroke, the DOM will be updated before the next event. For example,

What if I don’t want batch processing

In general, batch processing is safe, but some code may rely on getting something from the DOM immediately after a status update. Reactdom.flushsync () is used to exit batch processing

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() = > {
    setCounter(c= > c + 1);
  });
  // React has updated the DOM by now
  flushSync(() = > {
    setFlag(f= >! f); });// React has updated the DOM by now
}
Copy the code

We don’t want this to become common practice

Whether this will affect the function of hook

If you are using hook we expect batch processing to work (please let us know if it doesn’t)

Does this affect class functionality

Remember that updates in React time are always batched, so there is no impact on these updates.

So here are some boundary cases that we can talk about

handleClick = () = > {
  setTimeout(() = > {
    this.setState(({ count }) = > ({ count: count + 1 }));

    // { count: 1, flag: false }
    console.log(this.state);

    this.setState(({ flag }) = > ({ flag: !flag }));
  });
};
Copy the code

In Act18 this won’t be a problem, because even updates in setTimeout will be batched, React won’t render the first setState synchronously, the render will appear in the next frame of the browser, so the render hasn’t been updated yet

handleClick = () = > {
  setTimeout(() = > {
    this.setState(({ count }) = > ({ count: count + 1 }));

    // { count: 0, flag: false }
    console.log(this.state);

    this.setState(({ flag }) = > ({ flag: !flag }));
  });
};
Copy the code

Case.

If this prevents you from updating React18, you can use reactdom.flushsync to force the update, but we recommend caution

handleClick = () = > {
  setTimeout(() = > {
    ReactDOM.flushSync(() = > {
      this.setState(({ count }) = > ({ count: count + 1 }));
    });

    // { count: 1, flag: false }
    console.log(this.state);

    this.setState(({ flag }) = > ({ flag: !flag }));
  });
};
Copy the code

Case.

This problem does not affect function components that have hooks, because updating state does not update existing variables in useState.

function handleClick() {
  setTimeout(() = > {
    console.log(count); / / 0
    setCount(c= > c + 1);
    setCount(c= > c + 1);
    setCount(c= > c + 1);
    console.log(count); / / 0
  }, 1000)
Copy the code

While this behavior may surprise you when you use hooks, it paved the way for automatic batching.

aboutunstable_batchedUpdates

Batch multiple updates, typically in functions such as setTimeout that didn’t batch before React 18

import { unstable_batchedUpdates } from 'react-dom';

unstable_batchedUpdates(() = > {
  setCount(c= > c + 1);
  setFlag(f= >! f); });Copy the code

The API still exists in Act18, but it’s no longer needed because of automatic batching. We won’t remove it in version 18 for now, but it may be removed once the main React library no longer relies on it