Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for adding event listeners while in a write transaction #4446

Merged
merged 4 commits into from
Apr 25, 2022

Conversation

takameyer
Copy link
Contributor

@takameyer takameyer commented Mar 25, 2022

What, How & Why?

If a component is setup to render a collection that make use of both useQuery and useObject, it is possible to setup data updates in a way to set the event listeners while in a write transaction. This can occur if the writes are happing on the event loop (async) and multiple transactions occur one after the other with no break. As soon as the second write transaction is called, it will flush all event listener events. In the issue, the flushing of the event causes a component with useObject to render, which will apply an event listener on the object. This is not allowed within a write transaction.
The fix makes use of setImmediate, which will push the event listener apply function onto the event loop. Since, in this case, the write transaction is already on the event loop, the listener will be applied immediately after the write transaction has finished, but still allow the component to finish rendering.
This was also added to collections, as an object could possibly have a list property, and would end up having the same issue.

This closes # 4375

☑️ ToDos

  • 📝 Changelog entry
  • 📝 Compatibility label is updated or copied from previous entry
  • 🚦 Tests
  • 📱 Check the React Native/other sample apps work if necessary
  • 📝 Public documentation PR created or is not necessary
  • 💥 Breaking label has been applied or is not necessary

If this PR adds or changes public API's:

  • typescript definitions file is updated
  • jsdoc files updated
  • Chrome debug API is updated if API is available on React Native

@takameyer takameyer changed the title Add failing test for listener issue Fix for https://github.com/realm/realm-js/issues/4375 Apr 21, 2022
@takameyer takameyer changed the title Fix for https://github.com/realm/realm-js/issues/4375 Fix for adding event listeners while in a write transaction Apr 21, 2022
Make use of setImmediate to ensure event listeners are added after
write transactions have completed.
Update useQueryHook tests to give the event loop time to finish and add a listener.
Fix a test in useObject render to allow event listners to fire after writes.
@takameyer takameyer marked this pull request as ready for review April 21, 2022 06:17
// Add this on the next tick, in case there is a write transaction occuring immediately after creation of this object
// see https://github.com/realm/realm-js/issues/4375
setImmediate(() => {
if (object.isValid()) {
Copy link
Member

@kraenhansen kraenhansen Apr 21, 2022

Choose a reason for hiding this comment

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

Are you sure this check was actually needed? If yes - would a comment be appropriate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will throw if it's not valid

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a comment

Copy link
Member

@kneth kneth left a comment

Choose a reason for hiding this comment

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

Do we need to document the usage of setImmediate()? The origin of my question is the big yellow "note" in https://reactnative.dev/docs/timers.

packages/realm-react/CHANGELOG.md Outdated Show resolved Hide resolved
@@ -58,6 +58,8 @@ const useRealm = () => {

const useQuery = createUseQuery(useRealm);

const awaitEventLoop = () => new Promise((resolve) => setTimeout(resolve, 0));
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we add a comment summarising why we need to call this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm pushing an update to only use setImmediate when realm is in a write transaction. That will remove the need for this function and keep the tests understandable.

packages/realm-react/src/cachedCollection.ts Outdated Show resolved Hide resolved
// This replicates the issue https://github.com/realm/realm-js/issues/4375
it("will handle multiple async transactions", async () => {
const { queryByTestId } = await setupTest({ queryType, useUseObject: true });
const asyncEffect = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is a well known naming convention then no worries, but I thought this could be named more plainly

Suggested change
const asyncEffect = async () => {
const performTest = async () => {

takameyer and others added 2 commits April 21, 2022 14:24
Only use `setImmediate` if realm is in a write transaction.
Update comments to be more clear.
Co-authored-by: Tom Duncalf <tom.duncalf@mongodb.com>
@takameyer takameyer merged commit 69fcf8e into master Apr 25, 2022
@takameyer takameyer deleted the andrew/rr-listeners branch April 25, 2022 07:56
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants