Skip to content

Commit

Permalink
RJS-2648: Add ability for Realm instance to be used in RealmProvider (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gagik committed Jun 17, 2024
1 parent 31525c3 commit cbd975a
Show file tree
Hide file tree
Showing 6 changed files with 860 additions and 407 deletions.
34 changes: 33 additions & 1 deletion packages/realm-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,39 @@
* None

### Enhancements
* None
* Added the ability to use an existing Realm instance in `RealmProvider` and `createRealmContext`. ([#6714](https://github.com/realm/realm-js/pull/6714))
```jsx
// Using RealmProvider
import { RealmProvider } from "@realm/react";

const realm = new Realm(...);

function MyApp() {
return (
<RealmProvider realm={realm}>
...
</RealmProvider>
);
}

// Using createRealmContext
import { createRealmContext } from "@realm/react";

const realm = new Realm(...);
const { RealmProvider, useRealm } = createRealmContext(realm);

function MyApp() {
return (
<>
<RealmProvider>
...
</RealmProvider>
<AnotherComponent>
{/* Note: The hooks returned from `createRealmContext` using an existing Realm can be used outside of the scope of the provider! */}
</AnotherComponent>
</>
);
```
### Fixed
* <How to hit and notice issue? what was the impact?> ([#????](https://github.com/realm/realm-js/issues/????), since v?.?.?)
Expand Down
196 changes: 196 additions & 0 deletions packages/realm-react/src/RealmContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2024 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

import { createUseObject } from "./useObject";
import { createUseQuery } from "./useQuery";
import { createUseRealm } from "./useRealm";
import {
DynamicRealmProvider,
RealmProviderFromConfiguration,
RealmProviderFromRealm,
createRealmProvider,
} from "./RealmProvider";
import { createContext } from "react";
import Realm from "realm";

export type RealmContext<RealmProvider = DynamicRealmProvider> = {
/**
* The Provider component that is required to wrap any component using
* the Realm hooks.
* @example
* ```
* const AppRoot = () => {
* const syncConfig = {
* flexible: true,
* user: currentUser
* };
*
* return (
* <RealmProvider schema={[Task, User]} path={"data.realm"} sync={syncConfig}>
* <App/>
* </RealmProvider>
* )
* }
* ```
* @param props - The {@link Realm.Configuration} or {@link Realm} of the provider
* are set based on the options passed to `createRealmProvider`. When using a
* {@link Realm.Configuration}, individual config keys can be overridden when
* creating a `<RealmProvider>` by passing them as props. For example, to override
* the `path` config value, use a prop named `path` e.g., `path="newPath.realm"` an
* attribute of the same key.
*/
RealmProvider: RealmProvider;
/**
* Returns the instance of the {@link Realm} opened by the `RealmProvider`.
* @example
* ```
* const realm = useRealm();
* ```
* @returns a realm instance
*/
useRealm: ReturnType<typeof createUseRealm>;

/**
* Returns a {@link Realm.Collection} of {@link Realm.Object}s from a given type.
* The hook will update on any changes to any object in the collection.
*
* The result of this can be consumed directly by the `data` argument of any React Native
* VirtualizedList or FlatList. If the component used for the list's `renderItem` prop is {@link React.Memo}ized,
* then only the modified object will re-render.
* @example
* ```tsx
* // Return all collection items
* const collection = useQuery({ type: Object });
*
* // Return all collection items sorted by name and filtered by category
* const filteredAndSorted = useQuery({
* type: Object,
* query: (collection) => collection.filtered('category == $0',category).sorted('name'),
* }, [category]);
*
* // Return all collection items sorted by name and filtered by category, triggering re-renders only if "name" changes
* const filteredAndSorted = useQuery({
* type: Object,
* query: (collection) => collection.filtered('category == $0',category).sorted('name'),
* keyPaths: ["name"]
* }, [category]);
* ```
* @param options
* @param options.type - The object type, depicted by a string or a class extending Realm.Object
* @param options.query - A function that takes a {@link Realm.Collection} and returns a {@link Realm.Collection} of the same type. This allows for filtering and sorting of the collection, before it is returned.
* @param options.keyPaths - Indicates a lower bound on the changes relevant for the hook. This is a lower bound, since if multiple hooks add listeners (each with their own `keyPaths`) the union of these key-paths will determine the changes that are considered relevant for all listeners registered on the collection. In other words: A listener might fire and cause a re-render more than the key-paths specify, if other listeners with different key-paths are present.
* @param deps - An array of dependencies that will be passed to {@link React.useMemo}
* @returns a collection of realm objects or an empty array
*/
useQuery: ReturnType<typeof createUseQuery>;
/**
* Returns a {@link Realm.Object} from a given type and value of primary key.
* The hook will update on any changes to the properties on the returned object
* and return null if it either doesn't exists or has been deleted.
* @example
* ```
* const object = useObject(ObjectClass, objectId);
* ```
* @param type - The object type, depicted by a string or a class extending {@link Realm.Object}
* @param primaryKey - The primary key of the desired object which will be retrieved using {@link Realm.objectForPrimaryKey}
* @param keyPaths - Indicates a lower bound on the changes relevant for the hook. This is a lower bound, since if multiple hooks add listeners (each with their own `keyPaths`) the union of these key-paths will determine the changes that are considered relevant for all listeners registered on the object. In other words: A listener might fire and cause a re-render more than the key-paths specify, if other listeners with different key-paths are present.
* @returns either the desired {@link Realm.Object} or `null` in the case of it being deleted or not existing.
*/
useObject: ReturnType<typeof createUseObject>;
};

/**
* Creates Realm React hooks and Provider component for a given Realm configuration
* @example
* ```
*class Task extends Realm.Object {
* ...
*
* static schema: ObjectSchema = {
* name: 'Task',
* primaryKey: '_id',
* properties: {
* ...
* },
* };
*}
*
*const {useRealm, useQuery, useObject, RealmProvider} = createRealmContext({schema: [Task]});
* ```
* @param realmConfig - {@link Realm.Configuration} used to open the Realm
* @returns An object containing a `RealmProvider` component, and `useRealm`, `useQuery` and `useObject` hooks
*/
export function createRealmContext(realmConfig: Realm.Configuration): RealmContext<RealmProviderFromConfiguration>;
/**
* Creates Realm React hooks and Provider component for a given Realm instance.
*
* **Note:** the hooks returned from `createRealmContext` using an existing Realm can be used outside of the scope of the provider.
* @example
* ```
* const realm = new Realm({ schema: [...] });
* const {useRealm, useQuery, useObject, RealmProvider} = createRealmContext(realm);
* ```
* @param realm - {@link Realm} instance
* @returns An object containing a `RealmProvider` component, and `useRealm`, `useQuery` and `useObject` hooks
*/
export function createRealmContext(realm: Realm): RealmContext<RealmProviderFromRealm>;
/**
* Creates Realm React hooks and Provider component.
* @example
* ```
* class Task extends Realm.Object {
* ...
* static schema: ObjectSchema = {
* name: 'Task',
* primaryKey: '_id',
* properties: {
* ...
* },
* };
* }
* const {useRealm, useQuery, useObject, RealmProvider} = createRealmContext();
* ...
* <RealmProvider schema={[Task]}></RealmProvider>
* ```
* @example
* ```
* const realm = await Realm.open({ path: "example.realm", schema: [Task] });
* const {RealmProvider} = createRealmContext();
* ...
* <RealmProvider realm={realm}></RealmProvider>
* ```
* @returns An object containing a `RealmProvider` component, and `useRealm`, `useQuery` and `useObject` hooks
*/
export function createRealmContext(): RealmContext<DynamicRealmProvider>;
export function createRealmContext(
realmOrConfig?: Realm | Realm.Configuration,
): RealmContext<RealmProviderFromConfiguration | RealmProviderFromRealm | DynamicRealmProvider> {
const RealmContext = createContext<Realm | null>(realmOrConfig instanceof Realm ? realmOrConfig : null);
const RealmProvider = createRealmProvider(realmOrConfig, RealmContext);

const useRealm = createUseRealm(RealmContext);
const useQuery = createUseQuery(useRealm);
const useObject = createUseObject(useRealm);

return {
RealmProvider,
useRealm,
useQuery,
useObject,
};
}
Loading

0 comments on commit cbd975a

Please sign in to comment.