diff --git a/examples/web/src/__tests__/providers-hooks.js b/examples/web/src/__tests__/providers-hooks.js new file mode 100644 index 0000000000..42cc54ea59 --- /dev/null +++ b/examples/web/src/__tests__/providers-hooks.js @@ -0,0 +1,25 @@ +// :snippet-start:use-providers +import React from "react"; +import { APP_ID } from "../realm.config.json"; +import { AppProvider, UserProvider, RealmProvider, useRealm, useUser } from "@realm/react"; + +export const AppWrapper = () => { + return ( + {/* pass in your App ID as a prop */} + + + {/* call any app components here */} + + + + ); + }; +// :snippet-end: + +// export const TestComponent = () => { +// const user = useUser(); // hooks called at top level of a component declaration + +// return ( +// user?.id +// ); +// }; \ No newline at end of file diff --git a/source/examples/generated/react/providers-hooks.snippet.use-providers.js b/source/examples/generated/react/providers-hooks.snippet.use-providers.js new file mode 100644 index 0000000000..c6f6567bb6 --- /dev/null +++ b/source/examples/generated/react/providers-hooks.snippet.use-providers.js @@ -0,0 +1,15 @@ +import React from "react"; +import { APP_ID } from "../realm.config.json"; +import { AppProvider, UserProvider, RealmProvider, useRealm, useUser } from "@realm/react"; + +export const AppWrapper = () => { + return ( + {/* pass in your App ID as a prop */} + + + {/* call any app components here */} + + + + ); + }; diff --git a/source/frameworks.txt b/source/frameworks.txt index eb5aeacefe..e149bc4ed1 100644 --- a/source/frameworks.txt +++ b/source/frameworks.txt @@ -13,19 +13,21 @@ Build with Frameworks .. toctree:: :titlesonly: + React + React Native + Next.js Electron Flutter Maui - .NET - React Native SwiftUI The following pages contain information about building with specific frameworks using Atlas Device SDK: +- :ref:`sdks-build-with-react` +- :ref:`sdks-build-with-react-native` +- :ref:`sdks-build-with-nextjs` - :ref:`sdks-build-with-electron` - :ref:`sdks-build-with-flutter` - :ref:`sdks-build-with-maui` -- :ref:`sdks-build-with-dotnet` -- :ref:`sdks-build-with-react-native` -- :ref:`sdks-build-with-swiftui` +- :ref:`sdks-build-with-swiftui` \ No newline at end of file diff --git a/source/frameworks/react.txt b/source/frameworks/react.txt new file mode 100644 index 0000000000..910f2507ff --- /dev/null +++ b/source/frameworks/react.txt @@ -0,0 +1,30 @@ +.. _sdks-build-with-react: + +================ +Build with React +================ + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +.. toctree:: + :titlesonly: + + Install + Quick Start + Providers & Hooks + Model Data + Open & Manage Database Files + Read & Write Data + React to Changes + Access Atlas + Manage Users + Sync Data + Test & Debug + API Reference + +Placeholder page for information about building with React. (This may +be a directory depending on how much content we have/need.) \ No newline at end of file diff --git a/source/frameworks/react/providers-hooks.txt b/source/frameworks/react/providers-hooks.txt new file mode 100644 index 0000000000..52eefe5027 --- /dev/null +++ b/source/frameworks/react/providers-hooks.txt @@ -0,0 +1,787 @@ +.. _sdks-react-providers-hooks: + +================= +Providers & Hooks +================= + +.. meta:: + :description: Develop apps using the Providers and Hooks available in the @realm/react library. + :keywords: Realm, Javascript SDK, React, code example + +.. facet:: + :name: genre + :values: reference + +.. facet:: + :name: programming_language + :values: javascript + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +The ``@realm/react`` library offers custom React components that eliminate the boilerplate needed to +develop a React app. The components leverage the Provider design pattern to manage user +creation, user authentication, and data management. + +- ``RealmProvider``: Work with the configured database. + +- ``AppProvider``: Connect to your App Client for user authentication, only necessary when + using Device Sync. + +- ``UserProvider``: Access to the logged-in user object, only necessary when using Device Sync. + +The Providers are available to all frameworks used to build with the JavaScript SDK. + +Getting Started with Providers +------------------------------ + +Like all React components, you call Providers using html opening and closing tags. Nesting a +component within in another component's tags creates a parent-child relationship between them, +where child components can access the context created by its parent component. + +Props +~~~~~ + +Components take parameters called Props as input, passed into the opening tag. The props passed +into a parent component help create the context inherited by the components it wraps. Each +Provider has different props you can use for configuration. + +.. tabs:: + + .. tab:: RealmProvider Props + :tabid: realm-provider-props + + All properties of :realm-react-sdk:`BaseConfiguration ` can be passed as props. + + The most common BaseConfiguration properties used are: + + - ``schema?: (RealmObjectConstructor | ObjectSchema)[]`` + + Specifies all the object schemas in this Realm. Required when first creating a Realm. + If omitted, the schema will be read from the existing Realm file. + + - ``sync?: SyncConfiguration`` + + Configures a synced realm. + + ``RealmProvider`` has more props that define its behavior: + + - ``fallback?: React.ComponentType | React.ReactElement | null | undefined`` + + The fallback component to render while the Realm is opening. + + - ``closeOnUnmount?: boolean`` + + Default is ``true``. If set to ``false``, realm will not close when the + component unmounts. + + - ``realmRef?: React.MutableRefObject`` + + A ref to the realm instance. This is useful if you need to access the realm + instance outside of the scope of the realm. + + - ``children: React.ReactNode`` + + .. tab:: AppProvider Props + :tabid: app-provider-props + + All properties of :realm-react-sdk:`AppConfiguration + ` can be passed as props to ``AppProvider``. + + The most common AppConfiguration property used is: + + - ``id: string`` + + Specifies the App ID. + + .. tab:: UserProvider Props + :tabid: user-provider-props + + - ``fallback?: React.ComponentType | React.ReactElement | null | undefined`` + + The fallback component to render if there is no authorized user. This can be + used to render a log in screen or otherwise handle authentication. + +Configure your Providers +~~~~~~~~~~~~~~~~~~~~~~~~ + +This section details how to configure and expose a single realm using a ``RealmProvider`` +imported directly from ``@realm/react``. For information about using ``createRealmContext()`` +to configure a realm or exposing more than one realm, refer to their respective sections below. + +.. tabs:: + + .. tab:: Configure realm with sync + :tabid: configure-sync-realm + + If you are developing an app using sync, you will need to use all three Providers. + + By default, Realm syncs all data from the server before returning anything. If you want + to sync data in the background, read :ref:`Configure a Synced Realm While Offline + `. + + To configure a synced realm: + + 1. Import ``RealmProvider``, ``AppProvider``, and ``UserProvider`` from ``@realm/react``. + + 2. Configure ``AppProvider``. + + a. Pass your App ID string to the ``id`` prop of the ``AppProvider``. + + 3. Configure ``UserProvider`` and nest it within ``AppProvider``. + + a. Pass a component that logs a user in into the ``fallback`` prop. The app renders this component if there is no authenticated user. + + 4. Configure ``RealmProvider`` for sync and nest it within ``UserProvider``. + + a. Pass your object models to the ``schema`` prop. + + b. Pass your sync properties into the ``sync`` prop. Your sync properties are formatted like a json dictionary. + + c. Add other Configuration object properties as props to ``RealmProvider``. + + d. You must set up a sync subscription. The example below uses an initial subscription, + but you can also set up subscriptions in ``RealmProvider`` child components. + + .. include:: /includes/note-rn-multiple-app-clients-and-app-config-cache.rst + + Once your Providers have been configured, nest your app components within the + ``RealmProvider``. The rendering of each component is dependent on the successful + execution of its parent components' functionality. For example, if ``AppProvider`` cannot + connect to your app's App Services backend, the components it wraps will not render. + + You *must* nest the Providers and app components as shown below to ensure each component + has access to its required context: + + .. literalinclude:: /examples/generated/react-native/ts/configure-realm-sync.test.snippet.configure-realm-sync-full.tsx + :language: javascript + + .. tab:: Configure realm without sync + :tabid: configure-non-sync-realm + + If you are developing an app without sync, you only need to use ``RealmProvider``. + + To configure a non-synced realm: + + #. Import ``RealmProvider`` from ``@realm/react``. + + #. Pass your object models to the ``schema`` prop. (link out) + + #. Add other Configuration object properties as props to ``RealmProvider`` + + Once your ``RealmProvider`` has been configured, nest your app components within it to + give them access to the realm opened. + + .. literalinclude:: /examples/generated/react-native/ts/configure-realm-local.test.snippet.configure-realm.tsx + :language: typescript + + +Working with Providers using Hooks +---------------------------------- + +To use the Provider's context in your app's components, you can use :react.dev:`hooks `. + +Hooks act as functions used to access states in your app. React offers built-in hooks you can +use either on their own or to build custom hooks. + +There are two important rules to consider when working with hooks: + +- Hooks can only be used at the top level of a React component. +- Hooks can only be called in a React component or a custom hook, not in regular JavaScript + functions. + +The ``@realm/react`` library has custom hooks for each Provider you can import and +use in any wrapped component. + +.. tabs:: + + .. tab:: RealmProvider Hooks + :tabid: realm-provider-hooks + + .. _react-native-use-realm-hook: + + useRealm() + ~~~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useRealm(): Realm + + The ``useRealm()`` hook returns an opened realm instance. The realm instance + gives you access to realm methods and properties. For example, you can call + ``realm.write()`` to add a realm object to your realm. + + To learn more about modifying Realm data, refer to :ref:`Write Transactions + `. + + .. literalinclude:: /examples/generated/react-native/ts/create-test.test.snippet.crud-create-object.tsx + :language: typescript + :emphasize-lines: 3, 6-8, 15 + + *Returns* + + - ``Realm`` + Returns a realm instance. This is the realm created by the hook's parent, + ``RealmProvider``. + + .. _react-native-use-object-hook: + + useObject() + ~~~~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useObject(type, primaryKey): T & Realm.Object | null + + The ``useObject()`` hook returns a Realm object for a given + :ref:`primary key `. You can pass an object class + or the class name as a string and the primary key. + + The ``useObject()`` method returns null if the object doesn't exist or you have + deleted it. The hook will automatically subscribe to updates and rerender the + component using the hook on any change to the object. + + .. literalinclude:: /examples/generated/react-native/ts/read.test.snippet.crud-read-object-by-id.tsx + :language: typescript + + *Parameters* + + - ``type: string`` + A string that matches your object model's class name or a reference to a + class that extends :js-sdk:`Realm.Object `. + - ``primaryKey: T[keyof T]`` + The primary key of the desired object. + + *Returns* + + - ``Realm.Object | null`` + A Realm Object or ``null`` if no object is found. + + .. _react-native-use-query-hook: + + useQuery() + ~~~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useQuery(type, query?, deps?): Realm.Results> + + The ``useQuery()`` hook returns a collection of realm objects of a given type. + These are the results of your query. A query can be an object class or the class + name as a string. + + The ``useQuery()`` method subscribes to updates to any objects in the collection + and rerenders the component using it on any change to the results. + + You can use ``.filtered()`` and ``.sorted()`` to filter and sort your query + results. You should do this in the ``query`` argument of ``useQuery`` so that + they only run when there are changes in the dependency array. For more examples, + refer to the :ref:`CRUD - Read ` docs. + + .. literalinclude:: /examples/generated/react-native/ts/read.test.snippet.crud-read-filter-data.tsx + :language: typescript + :emphasize-lines: 3-5, 8-14 + + *Parameters* + + - ``type: string`` + A string that matches your object model's class name or a reference to a + class that extends :js-sdk:`Realm.Object `. + - ``query?: QueryCallback`` + A query function that can filter and sort query results. Builds on + ``useCallback`` to memoize the query function. + - ``deps?: DependencyList`` + A list of query function dependencies that's used to memoize + the query function. + + *Returns* + + - ``Realm.Results`` + A Realm Object or ``null`` if no object is found. + + .. _react-native-realm-context: + + .. tab:: AppProvider Hooks + :tabid: app-provider-hooks + + .. _react-native-use-auth: + + useApp() + ~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useApp(): Realm.App + + *Example* + + The ``useApp()`` hook provides access to a :js-sdk:`Realm.App ` + instance. + + .. include:: /examples/generated/react-native/v12/use-app.snippet.import-use-app.tsx.rst + .. include:: /examples/generated/react-native/v12/use-app.snippet.use-app.tsx.rst + + *Returns* + + - ``Realm.App`` + An instance of the current ``Realm.App`` created by ``AppProvider``. + + useAuth() + ~~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useAuth(): UseAuth + + ``useAuth`` returns ``result`` and an authentication method you specify, as shown below. There is an authentication + method for every App Services authentication provider. + + result + `````` + + .. code:: typescript + :copyable: false + :caption: Type signature + + result: AuthResult + + Result of authentication hook operation. For example, ``result.operation`` gives + you the name of the current operation. + + *Enum values* + + - ``state``. Can be "not-started", "pending", "success", "error". + + - ``operation``. For a list of all operation names, refer to the + :realm-react-sdk:`API documentation `. + + - ``pending``. Can be ``true`` or ``false``. + + - ``success``. Can be ``true`` or ``false``. + + - ``error``. Error-based object or undefined. + + Authentication Methods + `````````````````````` + + Below is a list of authentication methods and their Type signatures. An example of using + an authentication method is provided for ``logIn(credentials)``. You use the other authentication + methods in the same way. + + .. glossary:: + + logIn(credentials) + .. code:: typescript + :copyable: false + :caption: Type signature + + logIn(credentials: Realm.Credentials): void + + *Parameters* + + - ``credentials``. A Realm credential supplied by any supported Realm + authentication. + + *Example* + + Logs in a user with any authentication mechanism supported by + Realm. If called when a user is logged in, the current user switches to + the new user. Usually, it's better to use the more specific login + methods. + + .. code:: typescript + + const {logIn, result} = useAuth(); + + useEffect(() => logIn(Realm.Credentials.anonymous()), []); + + if(result.pending) { + return () + } + + if(result.error) { + return () + } + + if(result.success) { + return () + } + //... + + + logInWithAnonymous() + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithAnonymous(): void + + logInWithApiKey(key) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithApiKey(key: string): void + + *Parameters* + + - ``key``. A string that is linked to an App Services user. + + + logInWithEmailPassword(credentials) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithEmailPassword(credentials: { + email: string; + password: string; + }): void + + *Parameters* + + - ``credentials``. An object with ``email`` and ``password`` fields. + + logInWithJWT(credentials) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithJWT(token: string): void + + *Parameters* + + - ``credentials``. A string representation of a user's JWT. + + logInWithGoogle(credentials) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithGoogle(credentials: { + idToken: string; + } | { + authCode: string; + }): void; + + *Parameters* + + - ``credentials``. An object with an ``idToken`` or ``authCode`` field that + should contain the string token you get from Google Identity Services. + + logInWithApple(idToken) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithApple(idToken: string): void; + + *Parameters* + + - ``idToken``. A string you get from the Apple SDK. + + logInWithFacebook(accessToken) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithFacebook(accessToken: string): void; + + *Parameters* + + - ``accessToken``. A string you get from the Facebook SDK. + + logInWithFunction(payload) + .. code:: typescript + :copyable: false + :caption: Type signature + + logInWithFunction>(payload: PayloadType): void; + + *Parameters* + + - ``payload``. An object that contains user information you want to pass to + the App Services function that processes log in requests. + + logOut() + .. code:: typescript + :copyable: false + :caption: Type signature + + logOut(): void; + + useEmailPasswordAuth() + ~~~~~~~~~~~~~~~~~~~~~~ + + ``useEmailPasswordAuth`` returns ``result`` and an authentication method you specify, as + shown below. + + result + `````` + + .. code:: typescript + :copyable: false + :caption: Type signature + + result: AuthResult + + Result of authentication hook operation. For example, ``result.operation`` gives + you the name of the current operation. + + *Enum values* + + - ``state``. Can be "not-started", "pending", "success", "error". + + - ``operation``. For a list of all operation names, refer to the + :realm-react-sdk:`API documentation `. + + - ``pending``. Can be ``true`` or ``false``. + + - ``success``. Can be ``true`` or ``false``. + + - ``error``. Error-based object or undefined. + + Authentication Methods + ````````````````` + + Below is a list of authentication methods and their Type signatures. An example of using + an authentication method is provided for ``logIn(credentials)``. You use the other authentication + methods in the same way. + + .. glossary:: + + logIn(credentials) + .. code:: typescript + :copyable: false + :caption: Type signature + + logIn(credentials: { email: string; password: string }): void; + + *Parameters* + + - ``credentials``. An object that contains ``email`` and ``password`` properties. + + *Example* + + Logs a user in using an email and password. You could also call + ``logIn(Realm.Credentials.emailPassword(email, password))``. Returns a + ``Realm.User`` instance of the logged-in user. + + .. code:: typescript + + const {logIn, result} = useEmailPasswordAuth(); + + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const performLogin = () => { + logIn({email, password}); + }; + + if(result.pending) { + return () + } + + if(result.error) { + return () + } + + if(result.success) { + return () + } + //... + + logOut() + .. code:: typescript + :copyable: false + :caption: Type signature + + logOut(): void; + + register(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + register(args: { email: string; password: string }): void; + + *Parameters* + + - ``args``. An object that contains ``email`` and ``password`` properties. + + confirm(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + confirm(args: { token: string; tokenId: string }): void; + + *Parameters* + + - ``args``. An object that contains ``token`` and ``tokenId`` properties. + + resendConfirmationEmail(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + resendConfirmationEmail(args: { email: string }): void; + + *Parameters* + + - ``args``. An object that contains an ``email`` property. + + retryCustomConfirmation(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + retryCustomConfirmation(args: { email: string }): void; + + *Parameters* + + - ``args``. An object that contains an ``email`` property. + + sendResetPasswordEmail(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + sendResetPasswordEmail(args: { email: string }): void; + + *Parameters* + + - ``args``. An object that contains an ``email`` property. + + resetPassword(args) + .. code:: typescript + :copyable: false + :caption: Type signature + + resetPassword(args: { token: string; tokenId: string; password: string }): void; + + *Parameters* + + - ``args``. An object that contains ``token``, ``tokenId``, and ``password`` + properties. + + callResetPasswordFunction(args, restArgs) + .. code:: typescript + :copyable: false + :caption: Type signature + + callResetPasswordFunction( + args: { + email: string; + password: string; + }, + ...restArgs: Args + ): void; + + *Parameters* + + - ``args``. An object that contains ``email`` and ``password`` properties. + - ``restArgs``. Additional arguments that you need to pass to your custom + reset password function. + + .. tab:: UserProvider Hooks + :tabid: user-provider-hooks + + .. _react-native-use-user-hook: + + useUser() + ~~~~~~~~~ + + .. code:: typescript + :copyable: false + :caption: Type signature + + useUser(): Realm.User + + The ``useUser()`` hook provides access to the logged-in user. For example, + you can use ``useUser()`` to log the current user out. + + When changes to the user object happen, this hook will rerender its parent + component. For example, if you call ``user.refreshCustomData`` to get updated + custom user data, the ``useUser()`` parent component will rerender. + + .. include:: /examples/generated/react-native/v12/RealmWrapper.snippet.log-user-out.tsx.rst + + *Returns* + + - ``Realm.User`` + An instance of the currently-authenticated Realm user. + +Create Context with createRealmContext() +---------------------------------------- + +.. code:: typescript + :copyable: false + :caption: Type signature + + createRealmContext(realmConfig?): RealmContext + +Most of the time, you will only use ``createRealmContext()`` if you need to +configure more than one realm. Otherwise, you should import ``RealmProvider`` +and hooks directly from ``@realm/react``. + +The ``createRealmContext()`` method creates a :reactjs:`React Context +` object for a realm with a given +:realm-react-sdk:`Configuration `. The +``Context`` object contains the following: + +- A :reactjs:`Context Provider ` (referred to + as ``RealmProvider`` elsewhere) component that wraps around child components + and provides them with access to hooks. +- Various prebuilt :reactjs:`Hooks ` that access the + configured realm. + +For a detailed guide, refer to :ref:`Expose More Than One Realm +`. + +*Parameters* + +- ``realmConfig?: Realm.Configuration`` + + All properties of :realm-react-sdk:`BaseConfiguration + ` can be used. + +*Returns* + +- ``RealmContext`` + + An object containing a ``RealmProvider`` component, and the ``useRealm``, + ``useQuery`` and ``useObject`` hooks. + +Configure More Than One Realm +----------------------------- + +When you import ``RealmProvider`` from ``@realm/react``, that Provider has a +specific context and is associated with a single realm. If you need to configure +more than one realm, use ``createRealmContext()`` to instantiate a new Provider +for each realm. + +If you import ``useRealm()``, ``useQuery()``, or ``useObject()`` directly from +``@realm/react``, those hooks use the default realm context. To work with more +than one realm, you need to destructure a new realm Provider and its associated +hooks from the result of ``createRealmContext()``. You should namespace providers +to avoid confusion about which Provider and hooks you're working with. + +For a detailed guide, refer to :ref:`Expose More Than One Realm +`. + +For details about ``createRealmContext()``, refer to "Create Context with +createRealmContext()" on this page. \ No newline at end of file