diff --git a/examples/react-native/.eslintrc.js b/examples/react-native/.eslintrc.js index 2d4f5aa685..af888a5853 100644 --- a/examples/react-native/.eslintrc.js +++ b/examples/react-native/.eslintrc.js @@ -12,6 +12,7 @@ module.exports = { 'no-undef': 'off', 'no-new': 'off', 'jsx-quotes': 0, // do not remove this line, this removes the requirement for double quotes in jsx/tsx. The single quotes in jsx help bluehawk replace testIDs in the generated snippets for the docs + 'react-hooks/exhaustive-deps': 0, 'react/jsx-max-props-per-line': [0, {'maximum': 4, 'when': 'multiline'}], }, }; diff --git a/examples/react-native/__tests__/js/models/Cat.js b/examples/react-native/__tests__/js/models/Cat.js new file mode 100644 index 0000000000..9c7d4ac922 --- /dev/null +++ b/examples/react-native/__tests__/js/models/Cat.js @@ -0,0 +1,13 @@ +import Realm from 'realm'; +// :snippet-start: js-cat-schema +class Cat extends Realm.Object { + static schema = { + name: 'Cat', + properties: { + name: 'string', + birthDate: 'mixed', + }, + }; +} +// :snippet-end: +export default Cat; diff --git a/examples/react-native/__tests__/js/realm-database/schemas/mixed-tests.jsx b/examples/react-native/__tests__/js/realm-database/schemas/mixed-tests.jsx new file mode 100644 index 0000000000..504b6d243d --- /dev/null +++ b/examples/react-native/__tests__/js/realm-database/schemas/mixed-tests.jsx @@ -0,0 +1,149 @@ +import React, {useEffect} from 'react'; +import {Text, View} from 'react-native'; +import {render, waitFor} from '@testing-library/react-native'; +import Realm from 'realm'; +import {createRealmContext} from '@realm/react'; +import Cat from '../../models/Cat'; + +jest.setTimeout(30000); + +const realmConfig = { + schema: [Cat], + deleteRealmIfMigrationNeeded: true, +}; + +const {RealmProvider, useRealm, useQuery} = createRealmContext(realmConfig); + +let assertionRealm; + +describe('Mixed Tests', () => { + beforeEach(async () => { + // we will use this Realm for assertions to access Realm Objects outside of a Functional Component (like required by @realm/react) + assertionRealm = await Realm.open(realmConfig); + + // delete every object in the realmConfig in the Realm to make test idempotent + assertionRealm.write(() => { + assertionRealm.delete(assertionRealm.objects(Cat)); + + new Cat(assertionRealm, { + name: 'Clover', + birthDate: new Date('January 21, 2016'), + }); + }); + }); + afterAll(() => { + if (!assertionRealm.isClosed) { + assertionRealm.close(); + } + }); + it('should create an object with a mixed value', async () => { + // :snippet-start: create-mixed-object + // :replace-start: { + // "terms": { + // " testID='catItem'": "" + // } + // } + const CreateCatsInput = () => { + const realm = useRealm(); + + useEffect(() => { + // Add data to the Realm when the component mounts + realm.write(() => { + // create a Cat with a birthDate value of type string + new Cat(realm, { + name: 'Euler', + birthDate: 'December 25th, 2017', + }); + // create a Cat with a birthDate value of type date + new Cat(realm, { + name: 'Blaise', + birthDate: new Date('August 17, 2020'), + }); + + // create a Cat with a birthDate value of type int + new Cat(realm, {name: 'Euclid', birthDate: 10152021}); + + // create a Cat with a birthDate value of type null + new Cat(realm, {name: 'Pythagoras', birthDate: null}); + }); + }, []); + + // retrieve all cats + const cats = useQuery(Cat); + + return ( + <> + {cats.map(cat => ( + + {cat.name} + {String(cat.birthDate)} + + ))} + + ); + }; + // :replace-end: + // :snippet-end: + + const App = () => ( + + + + ); + + const {getAllByTestId} = render(); + + const catItems = await waitFor(() => getAllByTestId('catItem'), { + timeout: 5000, + }); + + // Test that 5 Cat Items have been added to the UI, + // and 5 matching Cat objects have been created in the assertionRealm + // (since there was already 1 cat object 'clover' created in the beforeEach) + // + the 4 new Cats + setTimeout(() => { + expect(catItems.length).toBe(5); + const cats = assertionRealm.objects(Cat); + expect(cats.length).toBe(5); + }, 5500); + }); + it('should query for objects with a mixed value', async () => { + // :snippet-start: query-mixed-object + // :replace-start: { + // "terms": { + // " testID='catBirthDate'": "" + // } + // } + const CatInfoCard = ({catName}) => { + // To query for the cat's birthDate, filter for their name to retrieve the realm object. + // Use dot notation to access the birthDate property. + const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0]; + const catBirthDate = cat.birthDate; + + if (cat) { + return ( + <> + {catName} + {String(catBirthDate)} + + ); + } else { + return Cat not found; + } + }; + // :replace-end: + // :snippet-end: + + const App = () => ( + + + + ); + const {getByTestId} = render(); + const catBirthDate = await waitFor(() => getByTestId('catBirthDate')); + // Expect catBirthDate in the UI to be the same value we set in the beforeEach (which is clover's birthday 'January 21, 2016') + expect(new Date(catBirthDate.props.children)).toStrictEqual( + new Date('January 21, 2016'), + ); + }); +}); diff --git a/examples/react-native/__tests__/ts/models/Cat.ts b/examples/react-native/__tests__/ts/models/Cat.ts new file mode 100644 index 0000000000..63e299b30e --- /dev/null +++ b/examples/react-native/__tests__/ts/models/Cat.ts @@ -0,0 +1,17 @@ +import Realm from 'realm'; +// TODO: Replace `static schema` with TS-first models + realm-babel-plugin (https://www.npmjs.com/package/@realm/babel-plugin) approach once realm-babel-plugin version 0.1.2 releases with bug fixes +// :snippet-start: ts-cat-schema +class Cat extends Realm.Object { + name!: string; + birthDate?: Realm.Mixed; + + static schema = { + name: 'Cat', + properties: { + name: 'string', + birthDate: 'mixed', + }, + }; +} +// :snippet-end: +export default Cat; diff --git a/examples/react-native/__tests__/ts/realm-database/schemas/mixed-test.tsx b/examples/react-native/__tests__/ts/realm-database/schemas/mixed-test.tsx new file mode 100644 index 0000000000..4139a8a929 --- /dev/null +++ b/examples/react-native/__tests__/ts/realm-database/schemas/mixed-test.tsx @@ -0,0 +1,149 @@ +import React, {useEffect} from 'react'; +import {Text, View} from 'react-native'; +import {render, waitFor} from '@testing-library/react-native'; +import Realm from 'realm'; +import {createRealmContext} from '@realm/react'; +import Cat from '../../models/Cat'; + +jest.setTimeout(30000); + +const realmConfig = { + schema: [Cat], + deleteRealmIfMigrationNeeded: true, +}; + +const {RealmProvider, useRealm, useQuery} = createRealmContext(realmConfig); + +let assertionRealm: Realm; + +describe('Mixed Tests', () => { + beforeEach(async () => { + // we will use this Realm for assertions to access Realm Objects outside of a Functional Component (like required by @realm/react) + assertionRealm = await Realm.open(realmConfig); + + // delete every object in the realmConfig in the Realm to make test idempotent + assertionRealm.write(() => { + assertionRealm.delete(assertionRealm.objects(Cat)); + + new Cat(assertionRealm, { + name: 'Clover', + birthDate: new Date('January 21, 2016'), + }); + }); + }); + afterAll(() => { + if (!assertionRealm.isClosed) { + assertionRealm.close(); + } + }); + it('should create an object with a mixed value', async () => { + // :snippet-start: create-mixed-object + // :replace-start: { + // "terms": { + // " testID='catItem'": "" + // } + // } + const CreateCatsInput = () => { + const realm = useRealm(); + + useEffect(() => { + // Add data to the Realm when the component mounts + realm.write(() => { + // create a Cat with a birthDate value of type string + new Cat(realm, { + name: 'Euler', + birthDate: 'December 25th, 2017', + }); + // create a Cat with a birthDate value of type date + new Cat(realm, { + name: 'Blaise', + birthDate: new Date('August 17, 2020'), + }); + + // create a Cat with a birthDate value of type int + new Cat(realm, {name: 'Euclid', birthDate: 10152021}); + + // create a Cat with a birthDate value of type null + new Cat(realm, {name: 'Pythagoras', birthDate: null}); + }); + }, []); + + // retrieve all cats + const cats = useQuery(Cat); + + return ( + <> + {cats.map(cat => ( + + {cat.name} + {String(cat.birthDate)} + + ))} + + ); + }; + // :replace-end: + // :snippet-end: + + const App = () => ( + + + + ); + + const {getAllByTestId} = render(); + + const catItems = await waitFor(() => getAllByTestId('catItem'), { + timeout: 5000, + }); + + // Test that 5 Cat Items have been added to the UI, + // and 5 matching Cat objects have been created in the assertionRealm + // (since there was already 1 cat object 'clover' created in the beforeEach) + // + the 4 new Cats + setTimeout(() => { + expect(catItems.length).toBe(5); + const cats = assertionRealm.objects(Cat); + expect(cats.length).toBe(5); + }, 5500); + }); + it('should query for objects with a mixed value', async () => { + // :snippet-start: query-mixed-object + // :replace-start: { + // "terms": { + // " testID='catBirthDate'": "" + // } + // } + const CatInfoCard = ({catName}: {catName: string}) => { + // To query for the cat's birthDate, filter for their name to retrieve the realm object. + // Use dot notation to access the birthDate property. + const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0]; + const catBirthDate = cat.birthDate; + + if (cat) { + return ( + <> + {catName} + {String(catBirthDate)} + + ); + } else { + return Cat not found; + } + }; + // :replace-end: + // :snippet-end: + + const App = () => ( + + + + ); + const {getByTestId} = render(); + const catBirthDate = await waitFor(() => getByTestId('catBirthDate')); + // Expect catBirthDate in the UI to be the same value we set in the beforeEach (which is clover's birthday 'January 21, 2016') + expect(new Date(catBirthDate.props.children)).toStrictEqual( + new Date('January 21, 2016'), + ); + }); +}); diff --git a/examples/react-native/testSetup.js b/examples/react-native/testSetup.js index d60624c7c8..ff2f65729d 100644 --- a/examples/react-native/testSetup.js +++ b/examples/react-native/testSetup.js @@ -7,3 +7,5 @@ global.console = { error: jest.fn(), warn: jest.fn(), }; + +jest.setTimeout(30000); diff --git a/source/examples/generated/react-native/js/Cat.snippet.js-cat-schema.js b/source/examples/generated/react-native/js/Cat.snippet.js-cat-schema.js new file mode 100644 index 0000000000..7b323b3d6a --- /dev/null +++ b/source/examples/generated/react-native/js/Cat.snippet.js-cat-schema.js @@ -0,0 +1,9 @@ +class Cat extends Realm.Object { + static schema = { + name: 'Cat', + properties: { + name: 'string', + birthDate: 'mixed', + }, + }; +} diff --git a/source/examples/generated/react-native/js/mixed-tests.snippet.create-mixed-object.jsx b/source/examples/generated/react-native/js/mixed-tests.snippet.create-mixed-object.jsx new file mode 100644 index 0000000000..73f910be56 --- /dev/null +++ b/source/examples/generated/react-native/js/mixed-tests.snippet.create-mixed-object.jsx @@ -0,0 +1,39 @@ +const CreateCatsInput = () => { + const realm = useRealm(); + + useEffect(() => { + // Add data to the Realm when the component mounts + realm.write(() => { + // create a Cat with a birthDate value of type string + new Cat(realm, { + name: 'Euler', + birthDate: 'December 25th, 2017', + }); + // create a Cat with a birthDate value of type date + new Cat(realm, { + name: 'Blaise', + birthDate: new Date('August 17, 2020'), + }); + + // create a Cat with a birthDate value of type int + new Cat(realm, {name: 'Euclid', birthDate: 10152021}); + + // create a Cat with a birthDate value of type null + new Cat(realm, {name: 'Pythagoras', birthDate: null}); + }); + }, []); + + // retrieve all cats + const cats = useQuery(Cat); + + return ( + <> + {cats.map(cat => ( + + {cat.name} + {String(cat.birthDate)} + + ))} + + ); +}; diff --git a/source/examples/generated/react-native/js/mixed-tests.snippet.query-mixed-object.jsx b/source/examples/generated/react-native/js/mixed-tests.snippet.query-mixed-object.jsx new file mode 100644 index 0000000000..aca8c8f71f --- /dev/null +++ b/source/examples/generated/react-native/js/mixed-tests.snippet.query-mixed-object.jsx @@ -0,0 +1,17 @@ +const CatInfoCard = ({catName}) => { + // To query for the cat's birthDate, filter for their name to retrieve the realm object. + // Use dot notation to access the birthDate property. + const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0]; + const catBirthDate = cat.birthDate; + + if (cat) { + return ( + <> + {catName} + {String(catBirthDate)} + + ); + } else { + return Cat not found; + } +}; diff --git a/source/examples/generated/react-native/ts/Cat.snippet.ts-cat-schema.ts b/source/examples/generated/react-native/ts/Cat.snippet.ts-cat-schema.ts new file mode 100644 index 0000000000..4e79383336 --- /dev/null +++ b/source/examples/generated/react-native/ts/Cat.snippet.ts-cat-schema.ts @@ -0,0 +1,12 @@ +class Cat extends Realm.Object { + name!: string; + birthDate?: Realm.Mixed; + + static schema = { + name: 'Cat', + properties: { + name: 'string', + birthDate: 'mixed', + }, + }; +} diff --git a/source/examples/generated/react-native/ts/mixed-test.snippet.create-mixed-object.tsx b/source/examples/generated/react-native/ts/mixed-test.snippet.create-mixed-object.tsx new file mode 100644 index 0000000000..73f910be56 --- /dev/null +++ b/source/examples/generated/react-native/ts/mixed-test.snippet.create-mixed-object.tsx @@ -0,0 +1,39 @@ +const CreateCatsInput = () => { + const realm = useRealm(); + + useEffect(() => { + // Add data to the Realm when the component mounts + realm.write(() => { + // create a Cat with a birthDate value of type string + new Cat(realm, { + name: 'Euler', + birthDate: 'December 25th, 2017', + }); + // create a Cat with a birthDate value of type date + new Cat(realm, { + name: 'Blaise', + birthDate: new Date('August 17, 2020'), + }); + + // create a Cat with a birthDate value of type int + new Cat(realm, {name: 'Euclid', birthDate: 10152021}); + + // create a Cat with a birthDate value of type null + new Cat(realm, {name: 'Pythagoras', birthDate: null}); + }); + }, []); + + // retrieve all cats + const cats = useQuery(Cat); + + return ( + <> + {cats.map(cat => ( + + {cat.name} + {String(cat.birthDate)} + + ))} + + ); +}; diff --git a/source/examples/generated/react-native/ts/mixed-test.snippet.query-mixed-object.tsx b/source/examples/generated/react-native/ts/mixed-test.snippet.query-mixed-object.tsx new file mode 100644 index 0000000000..eda7559b6c --- /dev/null +++ b/source/examples/generated/react-native/ts/mixed-test.snippet.query-mixed-object.tsx @@ -0,0 +1,17 @@ +const CatInfoCard = ({catName}: {catName: string}) => { + // To query for the cat's birthDate, filter for their name to retrieve the realm object. + // Use dot notation to access the birthDate property. + const cat = useQuery(Cat).filtered(`name = '${catName}'`)[0]; + const catBirthDate = cat.birthDate; + + if (cat) { + return ( + <> + {catName} + {String(catBirthDate)} + + ); + } else { + return Cat not found; + } +}; diff --git a/source/sdk/react-native/realm-database/schemas/mixed.txt b/source/sdk/react-native/realm-database/schemas/mixed.txt index 77e90dfaf8..659c1f5a82 100644 --- a/source/sdk/react-native/realm-database/schemas/mixed.txt +++ b/source/sdk/react-native/realm-database/schemas/mixed.txt @@ -6,22 +6,22 @@ Mixed - React Native SDK .. contents:: On this page :local: :backlinks: none - :depth: 2 + :depth: 1 :class: singlecol .. versionadded:: 10.5.0 Overview -------- -The mixed data type is a realm property type that can hold any valid Realm data type except a collection. -You can create collections (lists, sets, and dictionaries) of type ``mixed``, but a ``mixed`` itself -cannot be a collection. Properties using the mixed data type can also hold null values. +The mixed data type is a realm property type that can hold any valid Realm data +type except a collection. You can create collections (lists, sets, and +dictionaries) of type ``mixed``, but a ``mixed`` type itself cannot be a +collection. -.. note:: +The mixed data type is indexable, but you can't use it as a primary key. - The mixed data type is indexable, but you can't use it as a primary key. - Because null is a permitted value, you can't declare a Mixed property as - optional. +Properties using the mixed data type can also hold null values. You can't +declare a Mixed property as optional. Realm Object Models ------------------- @@ -29,25 +29,95 @@ To :ref:`set a property of your object model ` as ``Mixed``, set the property's type to "``mixed``". -.. literalinclude:: /examples/generated/node/data-types.snippet.define-mixed-in-schema.js - :language: javascript +.. tabs-realm-languages:: + + .. tab:: + :tabid: typescript + + .. literalinclude:: /examples/generated/react-native/ts/Cat.snippet.ts-cat-schema.ts + :language: typescript + + .. tab:: + :tabid: javascript + + .. literalinclude:: /examples/generated/react-native/js/Cat.snippet.js-cat-schema.js + :language: javascript + Create an Object With a Mixed Value ----------------------------------- -Create an object with a mixed value by running the :js-sdk:`realm.create() -` method within a write transaction. +Create an object with a mixed value by using the :mdn:`new +` operator within a :ref:`write +transaction `. + +Example +~~~~~~~ + +In the following ``CreateCatsInput`` example, we create several ``Cat`` realm +objects with a ``mixed`` type for the ``birthDate`` field. + +The ``CreateCatsInput`` component does the following: + +- Get access to the opened realm instance by calling the ``useRealm()`` hook. +- Use React's `useEffect `__ hook to call an anonymous function only once by passing ``useEffect`` an `empty dependency array `__ as a second argument. +- Within the anonymous function, we create four different ``Cat`` objects by using the ``new`` operator to create a new realm object within a write transaction. Each of the ``Cat`` objects uses a different data type for the ``birthDate`` property. +- Use the ``useQuery()`` hook to retrieve all ``Cat`` objects. +- `Map `__ through the cats to render a list of ``Text`` components displaying each cat's ``name`` and ``birthDate``. + + +.. tabs-realm-languages:: -.. literalinclude:: /examples/generated/node/data-types.snippet.create-objects-with-mixed-values.js - :language: javascript + .. tab:: + :tabid: typescript + + .. literalinclude:: /examples/generated/react-native/ts/mixed-test.snippet.create-mixed-object.tsx + :language: typescript + :emphasize-lines: 2, 6-23 + :linenos: + + .. tab:: + :tabid: javascript + + .. literalinclude:: /examples/generated/react-native/js/mixed-tests.snippet.create-mixed-object.jsx + :language: javascript + :emphasize-lines: 2, 6-23 + :linenos: Query for Objects with a Mixed Value ------------------------------------ -Query for objects with a mixed value by running the +To query for objects with a mixed value, run the :js-sdk:`Collection.filtered() ` method and -passing in a :ref:`filter ` for a non-mixed field. You can +pass in a :ref:`filter ` for a non-mixed field. You can then print the value of the mixed property or the entire object itself. -.. literalinclude:: /examples/generated/node/data-types.snippet.query-objects-with-mixed-values.js - :language: javascript +Example +~~~~~~~ + +In the following ``CatInfoCard`` example, we query for a ``Cat`` object using the +cat's name. + +The ``CatInfoCard`` component does the following: + +- Get all ``Cat`` objects by passing the ``Cat`` class to the ``useQuery()`` hook, and then use ``filtered()`` to filter the results to receive only the cats whose names match the name passed as a prop. We then get the first matching cat and store it as a const variable. +- Use dot notation to retrieve the mixed property, ``birthDate``. +- Display the cat's name and birthdate in the render method if Realm finds the cat. If there is no cat that matches the name passed into the component as a prop, we render text that says "Cat not found". + +.. tabs-realm-languages:: + + .. tab:: + :tabid: typescript + + .. literalinclude:: /examples/generated/react-native/ts/mixed-test.snippet.query-mixed-object.tsx + :language: typescript + :emphasize-lines: 4-5 + :linenos: + + .. tab:: + :tabid: javascript + + .. literalinclude:: /examples/generated/react-native/js/mixed-tests.snippet.query-mixed-object.jsx + :language: javascript + :emphasize-lines: 4-5 + :linenos: