Skip to content

Commit

Permalink
RJS-2861: Deprecate AppConfiguration.app and add app instance to App …
Browse files Browse the repository at this point in the history
…Provider (#6788)
  • Loading branch information
gagik authored Jul 16, 2024
1 parent 1d2ddd9 commit adda258
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 83 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Deprecations
* The callback for `SyncSession.addProgressNotification` taking `transferred` and `transferable` arguments is deprecated and will be removed. See **Enhancements** below for the new callback supporting both Flexible Sync and Partition-Based Sync. ([#6743](https://github.com/realm/realm-js/pull/6743))
* `AppConfiguration.app` is no longer used by Atlas Device Sync. It will be removed in future SDK releases and should not be used. ([#6785](https://github.com/realm/realm-js/issues/6785))

### Enhancements
* Added progress notifications support for Flexible Sync using an `estimate` as the new callback argument. The `estimate` is roughly equivalent to an estimated value of `transferred / transferable` in the deprecated Partition-Based Sync callback. ([#6743](https://github.com/realm/realm-js/pull/6743))
Expand Down
19 changes: 15 additions & 4 deletions packages/realm-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
## vNext (TBD)

### Deprecations
* None

### Enhancements
* None
* Added the ability to pass an existing `Realm.App` instance in `AppProvider` with the `app` prop. ([#6785](https://github.com/realm/realm-js/issues/6785))
```jsx
import { AppProvider } from "@realm/react";

const app = new Realm.App(...);

function MyApp() {
return (
<AppProvider app={app}>
...
</AppProvider>
);
}
```

### Fixed
* Fixed listener that was not being removed during unmounting of `useObject` and `useQuery` if the listener was added in a write transaction. ([#6552](https://github.com/realm/realm-js/pull/6552)) Thanks [@bimusiek](https://github.com/bimusiek)!
* The `app` prop in `AppProvider` meant for `LocalAppConfiguration` was not being used by Atlas Device Sync and has been removed. `app` is now only used to pass an existing `Realm.App` to the provider. ([#6785](https://github.com/realm/realm-js/pull/6785))

### Compatibility
* React Native >= v0.71.4
Expand Down
96 changes: 81 additions & 15 deletions packages/realm-react/src/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
////////////////////////////////////////////////////////////////////////////

import React, { createContext, useContext, useLayoutEffect, useRef, useState } from "react";
import Realm from "realm";
import Realm, { App } from "realm";
import isEqual from "lodash.isequal";

import { AuthResult, OperationState } from "./types";
import { RestrictivePick } from "./helpers";

type AppContextValue = Realm.App | null;

Expand Down Expand Up @@ -48,12 +49,8 @@ const AuthOperationProvider: React.FC<AuthOperationProps> = ({ children }) => {
);
};

/**
* Props for the AppProvider component. These replicate the options which
* can be used to create a Realm.App instance:
* https://www.mongodb.com/docs/realm-sdks/js/latest/Realm.App.html#~AppConfiguration
*/
type AppProviderProps = Realm.AppConfiguration & {
/** Mutually exclusive props for the AppProvider component when using a {@link Realm.AppConfiguration} */
type AppProviderWithConfigurationProps = Omit<Realm.AppConfiguration, "app"> & {
/**
* A ref to the App instance. This is useful if you need to access the App
* instance outside of a component that uses the App hooks.
Expand All @@ -62,23 +59,92 @@ type AppProviderProps = Realm.AppConfiguration & {
children: React.ReactNode;
};

/** Props the AppProvider component when using an existing {@link Realm.App} instance */
type AppProviderWithAppProps = {
app: Realm.App;
children: React.ReactNode;
};

/** Combined props for the AppProvider component, used when defining mutually exclusive props */
type AppProviderProps = AppProviderWithAppProps & AppProviderWithConfigurationProps;

/** Mutually exclusive props for the AppProvider component when using an existing {@link Realm.App} instance */
type DynamicAppProviderWithAppProps = RestrictivePick<AppProviderProps, keyof AppProviderWithAppProps>;

/** Mutually exclusive props for the AppProvider component when using a {@link Realm.AppConfiguration} */
type DynamicAppProviderWithConfigurationProps = RestrictivePick<
AppProviderProps,
keyof AppProviderWithConfigurationProps
>;

/**
* Props for the AppProvider component. You can either pass an existing {@link Realm.App} through the `app` prop
* or props that replicate the {@link Realm.AppConfiguration} that is used to create a Realm.App instance.
*/
type DynamicAppProviderProps = DynamicAppProviderWithAppProps | DynamicAppProviderWithConfigurationProps;

/**
* React component providing a Realm App instance on the context for the
* sync hooks to use. An `AppProvider` is required for an app to use the hooks.
* @param props - Either the {@link Realm.AppConfiguration} for App Services passed as props **or** the {@link Realm.App} passed through the `app` prop.
* @param appRef - Provides a ref to the app instance, which can be used to access the app instance outside of the React component tree. **Not available when using the `app` prop**.
*/
export function AppProvider(props: DynamicAppProviderProps): React.ReactNode;
/**
* React component providing a Realm App instance on the context for the
* sync hooks to use. An `AppProvider` is required for an app to use the hooks.
* @param appProps - The {@link Realm.AppConfiguration} for app services, passed as props.
* @param props - The {@link Realm.AppConfiguration} for App Services, passed as props.
* @param appRef - A ref to the app instance, which can be used to access the app instance outside of the React component tree.
*/
export const AppProvider: React.FC<AppProviderProps> = ({ children, appRef, ...appProps }) => {
const configuration = useRef<Realm.AppConfiguration>(appProps);
export function AppProvider(props: DynamicAppProviderWithConfigurationProps): React.ReactNode;
/**
* React component providing a Realm App instance on the context for the
* sync hooks to use. An `AppProvider` is required for an app to use the hooks.
* @param app - The {@link Realm.App} for the provider.
*/
export function AppProvider(props: DynamicAppProviderWithAppProps): React.ReactNode;
export function AppProvider({ children, app, ...config }: DynamicAppProviderProps): React.ReactNode {
if (app instanceof App) {
if (Object.keys(config).length > 0) {
throw new Error("Cannot use configuration props when using an existing App instance.");
}
return <AppProviderWithApp app={app}>{children}</AppProviderWithApp>;
} else if (typeof app !== "undefined") {
throw new Error(
`The "app" prop is used to pass an existing Realm.App instance into an AppProvider. Either remove it or pass a valid Realm.App.`,
);
}

const [app, setApp] = useState<Realm.App>(() => new Realm.App(configuration.current));
return (
<AppProviderWithConfiguration {...(config as AppProviderWithConfigurationProps)}>
{children}
</AppProviderWithConfiguration>
);
}

function AppProviderWithApp({ app, children }: React.PropsWithChildren<AppProviderWithAppProps>) {
return (
<AppContext.Provider value={app}>
<AuthOperationProvider>{children}</AuthOperationProvider>
</AppContext.Provider>
);
}

function AppProviderWithConfiguration({
appRef,
children,
...config
}: React.PropsWithChildren<AppProviderWithConfigurationProps>) {
const configRef = useRef<Realm.AppConfiguration>(config);

const [app, setApp] = useState<Realm.App>(() => new Realm.App(configRef.current));

// Support for a possible change in configuration
if (!isEqual(appProps, configuration.current)) {
configuration.current = appProps;
if (!isEqual(config, configRef.current)) {
configRef.current = config as Realm.AppConfiguration;

try {
setApp(new Realm.App(configuration.current));
setApp(new Realm.App(configRef.current));
} catch (err) {
console.error(err);
}
Expand All @@ -95,7 +161,7 @@ export const AppProvider: React.FC<AppProviderProps> = ({ children, appRef, ...a
<AuthOperationProvider>{children}</AuthOperationProvider>
</AppContext.Provider>
);
};
}

/**
* Hook to access the current {@link Realm.App} from the {@link AppProvider} context.
Expand Down
4 changes: 2 additions & 2 deletions packages/realm-react/src/RealmProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export type DynamicRealmProviderWithRealmProps = RestrictivePick<RealmProviderPr
/**
* Represents properties of a {@link DynamicRealmProvider} where Realm configuration props are set and Realm instance props are disallowed.
*/
export type DynamicsRealmProviderWithConfigurationProps = RestrictivePick<
export type DynamicRealmProviderWithConfigurationProps = RestrictivePick<
RealmProviderProps,
keyof RealmProviderConfigurationProps
>;
Expand All @@ -88,7 +88,7 @@ export type DynamicsRealmProviderWithConfigurationProps = RestrictivePick<
* Supports either {@link RealmProviderRealmProps} or {@link RealmProviderConfigurationProps}.
*/
export type DynamicRealmProvider = React.FC<
DynamicRealmProviderWithRealmProps | DynamicsRealmProviderWithConfigurationProps
DynamicRealmProviderWithRealmProps | DynamicRealmProviderWithConfigurationProps
>;

export function createRealmProviderFromRealm(
Expand Down
Loading

0 comments on commit adda258

Please sign in to comment.