realm / realm-js

Realm is a mobile database: an alternative to SQLite & key-value stores
https://realm.io
Apache License 2.0
5.67k stars 561 forks source link

Better Multi React DOM support #6233

Open aight8 opened 7 months ago

aight8 commented 7 months ago

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

kneth commented 7 months ago

@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.

aight8 commented 7 months ago

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:

Motivation

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

nicolaosm commented 5 months ago

Would love to see a solution for this.

kraenhansen commented 5 months ago

@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

gagik commented 1 week ago

@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.