Skip to content

Frame-lock external event handlers to allow for React batching#1988

Merged
mergetron[bot] merged 3 commits intomainfrom
fix/async-event-handlers
Mar 6, 2023
Merged

Frame-lock external event handlers to allow for React batching#1988
mergetron[bot] merged 3 commits intomainfrom
fix/async-event-handlers

Conversation

@mattgperry
Copy link
Copy Markdown
Collaborator

No description provided.

@mattgperry mattgperry requested a review from adamseckel March 1, 2023 13:48
@mattgperry mattgperry force-pushed the fix/async-event-handlers branch from e24e65d to d9d94e2 Compare March 5, 2023 09:24
@mattgperry mattgperry requested a review from iamakulov March 6, 2023 11:31
@iamakulov
Copy link
Copy Markdown

iamakulov commented Mar 6, 2023

As investigated privately, TL;DR on why batching sometimes didn’t work before this PR:

  • Motion uses native event listeners instead of React’s synthetic ones
  • When native event listeners fire, they fire as tasks (in terms of the task queue)
  • React only batches updates within a single task. That’s because, with React 18, batching works as follows:
    • you call setState()
    • setState() doesn’t render the update immediately. Instead, it pushes the update into React’s internal queue and schedules a microtask to process the queue
    • (if more setState()s happen, they also only push updates into the queue)
    • in the end of the task, the microtask gets invoked, and the update queue gets processed at once
  • So if we have multiple event listeners fire at the same time, and each of them calls setState(), these state updates won’t be batched because they happen in separate tasks

And why it works now:

  • Now, when native event listeners fire, we don’t call the event listeners from the userland (such as onTap) immediately. Instead, we wrap these calls with sync.update() (which is effectively a batched requestAnimationFrame())
  • When the requestAnimationFrame callback fires, we call all event listeners from the userland one-by-one, synchronously
  • This means all setState() calls inside these event listeners happen within a single task – and are batched correctly

Copy link
Copy Markdown

@iamakulov iamakulov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

@mergetron mergetron Bot merged commit 88a7f76 into main Mar 6, 2023
@mergetron mergetron Bot deleted the fix/async-event-handlers branch March 6, 2023 13:05
Copy link
Copy Markdown
Collaborator

@adamseckel adamseckel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants