realm-js icon indicating copy to clipboard operation
realm-js copied to clipboard

Better Multi React DOM support

Open aight8 opened this issue 2 years ago • 5 comments

Problem

I use react-native-navigation, which uses many React DOM's (every Screen is a separate DOM) Every screen shares a common Wrapper, which initializes Realm, App and optionally the User Context. The problem is that:

  1. AppProvider and RealmProvider creates and configure a fresh instance of Realm.App and Realm object for the DOM. I just want to initialize and reuse the global realm and realm.app object externally. Also the observing doesn't work like this, I don't know exactly why, but I think that the global realm and realm.app object only corresponds to the last changed screen.
  2. I have tried to simply set Realm and Realm.App through the Context.Provider but they are not exported. Also, the AppProvider contains some extra logic (also renders AuthOperationProvider).
<AppProvider
	appRef={appRef}
	id={'XXXXX'}
>
	<ConditionalWrap
		condition={userRequired}
		wrap={(children) => (
			<UserProvider fallback={<Text>UserProvider fallback</Text>}>
				{children}
			</UserProvider>
		)}
	>
		<RealmProvider
			realmRef={realmRef}
			closeOnUnmount={false}
			deleteRealmIfMigrationNeeded={true}
			schema={[Model]}
		>
			{children}
		</RealmProvider>
	</ConditionalWrap>
</AppProvider>

Solution

const realmApp = new Realm.App({})
const realm = new Realm({})

// Here I can setup the single instance:

realmApp.addEventListener(() => {
    // something
})
realmApp.currentUser?.addEventListener(() => {
    // change Screen (e.g. login screen)
})
<AppProvider
	...
	appInstance={realmApp}
	...
>
	<ConditionalWrap
		condition={userRequired}
		wrap={(children) => (
			<UserProvider fallback={<Text>UserProvider fallback</Text>}>
				{children}
			</UserProvider>
		)}
	>
		<RealmProvider
			...
			realmInstance={realm} // explicitly also closeOnUnmount
			...
		>
			{children}
		</RealmProvider>
	</ConditionalWrap>
</AppProvider>

As result the Realm and Realm.App Object is managed externally and as side effect the code React DOM stays little bit cleaner.

Alternatives

There is no real alternatives. I currently forked realm-js, exported the React Context.Provider, manually add AuthOperationProvider etc.

How important is this improvement for you?

Dealbreaker

Feature would mainly be used with

Atlas Device Sync

aight8 avatar Nov 04 '23 11:11 aight8

@aight8 Thank you raising the issue. We don't have a quick fix but we use React Native Navigation on one of our examples. I will bring it up with the team to get some ideas.

kneth avatar Nov 08 '23 15:11 kneth

Hi thank you. I made it work by a fork and export createUseObject, createUseQuery, createUseRealm from '@realm/react' etc. (don't know if the list is complete).

Just for clearification of the problem (that we understand each other correctly), the examples uses react-navigation (single React DOM based), I use react-native-navigation (many React DOM based)

In the example it's like:

- AppProvider (App.tsx)
   - UserProvider (otherwise show Login component)
      - RealmProvider (AuthenticatedApp.tsx)
         - ... the whole app incl. navigation... 

A react-native-navigation based app is it like:

// HomeScreen
- AppProvider
   - UserProvider
      - RealmProvider

// SettingsScreen
- AppProvider
   - UserProvider
      - RealmProvider

// Tab1ContentScreen
- AppProvider
   - UserProvider
      - RealmProvider

// Overlay1Screen
- AppProvider
   - UserProvider
      - RealmProvider

// Modal1Screen
- AppProvider
   - UserProvider
      - RealmProvider

// TopBarHeaderScreen
- AppProvider
   - UserProvider
      - RealmProvider

// etc.

So think about, every Root View, Overlay/Modals are total separately rendered React DOM's. Some of the screens are rendered and visible at the same time. (e.g. simultaneously: Tab1ContentScreen, Top1ContentTopBarScreen, Modal1Screen)

This has the following consequences:

  • A realm can only be opened by one screen at a time anyway
  • And also if multiple connections were possible: AppProvider and RealmProvider creates Realm and Realm.App objects. It is sufficient to create them once, or is even desired to keep them synchronized (the events are also triggered separately everywhere)

Motivation

  • App's using react-native-navigation offer a particularly native feeling for the user. it would be nice if these applications could use realm.
  • concurrentRoot is coming -> https://reactnative.dev/docs/react-18-and-react-native

The easiest way to solve this issue, to let the user provide Realm and Realm.App object.

aight8 avatar Nov 08 '23 18:11 aight8

Would love to see a solution for this.

nicolaosm avatar Jan 06 '24 16:01 nicolaosm

@aight8 and @nicolaosm - wanted to mention that we're also tracking this issue for a similar feature that you might want to 👍 and watch: https://github.com/realm/realm-js/issues/6283

kraenhansen avatar Jan 07 '24 19:01 kraenhansen

@aight8 @nicolaosm

Realm React v0.8.0 now supports using an existing Realm instance with a RealmProvider or createRealmContext! Should provide a nice solution to this usecase.

gagik avatar Jun 18 '24 15:06 gagik

@aight8 @nicolaosm Realm React v0.9.0 now also supports providing an existing app to the AppProvider 🎆 This covers the entire suggested solution, would you consider the issue resolved?

gagik avatar Jul 17 '24 11:07 gagik

Closing this as the solution has been implemented in Realm React v0.9.0.

gagik avatar Jul 29 '24 15:07 gagik