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(examples): Fix React projects not providing correct client to app #1191

Closed
wants to merge 9 commits into from

Conversation

msfstef
Copy link
Contributor

@msfstef msfstef commented Apr 24, 2024

Since removing the global emitter, the web example stopped working, and the issue was that the isMounted logic that was placed there to work around double mounts from React's StrictMode was not doing what it was supposed to.

Effectively an Electric client was initialized and provided to the app, and then a second one was initialized (potentially with a second database as uniqueTabId is not guaranteed to provide the same ID for a tab, which is why I also moved that at the top level), but not provided to the app.

Since the registry is global, the second database connection opened is the one that the satellite process used, but the notifier is now separate for each electrified instance.

Perhaps we still want a unified event emitter for all electric instances in a single application, but I suggest we put that in the global registry rather than as a separate top-level global singleton that can't be cleaned up. What do you think @kevin-dp ? (see #1192 for reverting the change)

console.log(config)

const conn = await ElectricDatabase.init(scopedDbName)
const client = await electrify(conn, schema, config)
client = await electrify(conn, schema, config)
await client.connect(token)
Copy link
Contributor

Choose a reason for hiding this comment

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

The component could have re-rendered during any of the following three awaits.
The returned cleanup function should set an ignore var that is checked after each await, returning immediately if true. This would prevent any unnecessary work and setting the electric state var to the wrong instance.

This is sort of what the isMounted did.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the issue is that electrification affects global components like the registry - the isMounted that we had before (or any check of this kind that we add) will still likely cause a mixup.

Basically the first database connection opened is fed to electrify and starts a satellite process - even if the electrified instance is not set at that point with setElectric, subsequent calls will create new database connections that will be fed to the client but not the satellite as the satellite is global and will use the first one (and first notifier), see:

// If we're in the process of starting the satellite process for this
// dbName, then we short circuit and return that process. Note that
// this assumes that the previous call to start the process for this
// dbName would have passed in functionally equivalent `dbAdapter`,
// `fs` and `notifier` arguments. Which is *probably* a safe assumption
// in the case where this might happen, which is multiple components
// in the same app opening a connection to the same db at the same time.
const startingPromises = this.startingPromises
const starting = startingPromises[dbName]
if (starting !== undefined) {
return starting
}

This leads to a weird state where the client performs the writes and reads correctly but the satellite is still using the first notifier and adapter/connection.

I find it better to let it do the little additional work (which is only really done in React's StrictMode) and ensure that we end up with the correct setup.

An alternative route, which I think might also be a good solution, is to have a separate, outside of the component top-level initElectric which keeps track of whether it has been initialized or not, and the component doesn't need to worry about multiple calls and cleanups as much.

@msfstef
Copy link
Contributor Author

msfstef commented Apr 25, 2024

FWIW I'm happy to close this PR in favour of reinstating the global emitter.

@msfstef msfstef marked this pull request as draft April 25, 2024 13:32
@kevin-dp
Copy link
Contributor

kevin-dp commented Apr 25, 2024

These are my 2 cents after a discussion on this topic with Stefanos. I don't see why we want to initialize a DB connection and electrify it inside a UI component, every time that component is mounted/unmounted the DB has to be opened/closed. Setting up a DB connection doesn't seem like a UI concern to me so I would rather move it outside of the component and pass it through React Context into my components.

msfstef added a commit that referenced this pull request Apr 25, 2024
See #1191 for description
of issues that arose from removing the global emitter.

I'm opening this PR in the case we decide that we want to reintroduce it
as it ensures that all instantiated electric clients will share
notifications between them.
@samwillis
Copy link
Contributor

@kevin-dp I agree, and this all needs to change anyway when we have muti-tab. The database connection will need to be initiated inside the worker, not on the main thread. There are a few API options for this, but it's a significant change away from the existing initiation of electric, but also needs to be designed for single process too for things like React Native where there is no webworkers.

@msfstef msfstef closed this May 8, 2024
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.

None yet

3 participants