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

RJS-2861: Deprecate AppConfiguration.app and add app instance to App Provider #6788

Merged
merged 11 commits into from
Jul 16, 2024
Merged
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/pull/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
86 changes: 73 additions & 13 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,7 @@ 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
*/
gagik marked this conversation as resolved.
Show resolved Hide resolved
type AppProviderProps = 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,20 +58,84 @@ type AppProviderProps = Realm.AppConfiguration & {
children: React.ReactNode;
};

type AppProviderWithAppProps = {
app: Realm.App;
children: React.ReactNode;
};

type AppProviderProps = AppProviderWithAppProps & AppProviderWithConfigurationProps;
type DynamicAppProviderWithAppProps = RestrictivePick<AppProviderProps, keyof AppProviderWithAppProps>;
type DynamicAppProviderWithConfigurationProps = RestrictivePick<
AppProviderProps,
keyof AppProviderWithConfigurationProps
>;

/**
* Props for the AppProvider component. You can either pass an existing app through the `appInstance` prop
* or props that replicate the configuration that is used to create a Realm.App instance:
* https://www.mongodb.com/docs/realm-sdks/js/latest/Realm.App.html#~AppConfiguration
*/
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 appProps - 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 appInstance - The {@link Realm.App} for the provider.
*/
export function AppProvider(props: DynamicAppProviderWithAppProps): React.ReactNode;
export function AppProvider({ children, app, ...appWithConfigurationProps }: DynamicAppProviderProps): React.ReactNode {
gagik marked this conversation as resolved.
Show resolved Hide resolved
if (app instanceof App) {
if (Object.keys(appWithConfigurationProps).length > 0) {
throw new Error("Cannot use configuration props when using an existing App instance.");
}
return <AppProviderWithApp app={app}>{children}</AppProviderWithApp>;
} else if (app != null) {
gagik marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
`The "app" prop is used to use an existing Realm.App instance with an AppProvider. Either remove it or pass a valid Realm.App.`,
);
}

return (
<AppProviderWithConfiguration {...(appWithConfigurationProps 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,
...configurationProps
gagik marked this conversation as resolved.
Show resolved Hide resolved
}: React.PropsWithChildren<AppProviderWithConfigurationProps>) {
const configuration = useRef<Realm.AppConfiguration>(configurationProps);
gagik marked this conversation as resolved.
Show resolved Hide resolved

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

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

try {
setApp(new Realm.App(configuration.current));
Expand All @@ -95,7 +155,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
Loading