Repository: react
Issue: Feature request: A useLayoutEffect with read/write batching across a tree
Labels: Type: Feature Request,Component: Reconciler,Component: Hooks

Today, `useLayoutEffect` can be used for synchronous read/writes across the DOM.

```javascript
useLayoutEffect(() => {
  // Write
  ref.current.style.transform = ""

  // Read
  const box = ref.current.getBoundingClientRect()
})
```

For a single instance of a single component, this works well. But if this code is repeated or reused anywhere in the tree, we trigger  [layout thrashing](https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing). The severity of the layout thrashing scales linearly with the number of hooks/components featuring either this code or code like it.

Hooks and components are designed to be composable, yet it's this trivial to write one that isn't. 

Instead, what I'd like is a batched version of `useLayoutEffect` that provides `read` and `write` callbacks. These schedule callbacks that will be called:

1. Synchronously before paint
2. In "parallel" in reads/writes/reads etc
3. After all child components in the tree have run `useLayoutEffect` - including those entering the tree (currently these don't mount until after other `useLayoutEffect`s have been called)

It could look like this, though I'm more interested in the above specs than actual API:

```javascript
useBatchedLayoutEffect((read, write) => {
  write(() =>  {
    ref.current.style.transform = ""
  })

  read(() => {
    const box = ref.current.getBoundingClientRect()
  })
})
```

Then, adhering the 3 specifications above, these callbacks are executed in order, so all `read`s from across the tree, then all `write`s, then all `read`s etc. There is no upper limit for the number of permitted ping-ponged reads/writes IMO as the amount of layout thrashing you could possibly suffer will never be worse than the single hungriest hook. In my experience I've never needed more than a read/write/read/write.

## Measurement accuracy

In the given example, we're measuring a component after first resetting its `transform` because we want to snapshot its actual bounding box without any transforms applied. But if this component is [nested in itself](https://codesandbox.io/s/dazzling-varahamihira-2qiyw?file=/src/App.js), so both a parent and child are performing the same type of animation, we want to unset all the transforms before measuring any of the elements, otherwise the resulting measurements will be incorrect.

## What about `requestAnimationFrame`?

Theoretically it could be possible to implement this ourselves in userland by creating a batcher that executes on the next available frame. Sadly this isn't possible in practise. For whatever browser-internal reason it is necessary to run these reads and writes synchronously to prevent flashes of incorrectly-styled components.

If you play with this very simple layout animation implementation by clicking on the red box you'll see it suffers no flashes:

https://codesandbox.io/s/broken-star-cycfz?file=/src/App.js:528-537

But if you uncomment the wrapping `requestAnimationFrame` within the `useLayoutEffect` you'll see it does randomly flash with the undesired styles.