Mobile Bytes #9 : Realm React for React Native

Greetings Realm folks,

My name is Andrew Meyer, one of the engineers at realm-js, and I am making a guest post today in @henna.s Realm Byte column to help React Native users get started with our new library @realm/react. :smiley:

@realm/react is a module built on top of realm-js with the specific purpose of making it easier to implement Realm in React.

I wanted to provide a quick example for React Native developers, to get an idea of how easy it is to get started using Realm using @realm/react. Therefore, I made an 80 line example of how to create a simple task manager using the library.

You only need to have @realm/react and realm installed in your project and you will be good to go. If you aren’t using TypeScript, simply modify the Task class to not use types.

Here is a breakdown of the code.

Model Setup

Setting up and thinking about your model is the first step in getting any application off the ground. For our simple app, we are defining a Task model with a description, completion flag, and creation timestamp. It also contains a unique _id, which is the primary key of the Task model. It’s good to define a primary key, in case you want to reference a single Task in your code later on.

We have also added a generate method. This is a convenience function that we will use to create new tasks. It automatically generates a unique _id, sets the creation timestamp, and sets the description provided by its argument.

The schema property is also required for Realm. This defines the structure of the model and tells Realm what to do with the data. Follow Realm Object Model for more information.

Here is the code for setting up your model class:

class Task extends Realm.Object {
  _id!: Realm.BSON.ObjectId;
  description!: string;
  isComplete!: boolean;
  createdAt!: Date;

  static generate(description: string) {
    return {
      _id: new Realm.BSON.ObjectId(),
      description,
      createdAt: new Date(),
     };
  }

  static schema = {
    name: 'Task',
    primaryKey: '_id',
    properties: {
      _id: 'objectId',
      description: 'string',
      isComplete: { type: 'bool', default: false },
      createdAt: 'date'
    },
  };
}

Setting up the RealmProvider

The next part of the code is a necessary part in setting up your application to interact with Realm using hooks. In this code, we are calling createRealmContext which will return an object containing a RealmProvider and a set of hooks (useRealm, useQuery and useObject).

The RealmProvider must wrap your application in order to make use of the hooks. When the RealmProvider is rendered, it will use the configuration provided to the createRealmContext to open the Realm when it is rendered. Alternatively, you can set the configuration through props on RealmProvider.

Here is the code for setting up your application wrapper:

const { RealmProvider, useRealm, useQuery } = createRealmContext({ schema: [Task] })

export default function AppWrapper() {
  return (
    <RealmProvider><TaskApp /></RealmProvider>
  )
}

Application Component

Now that you have an idea of how to set everything up, let’s move on to the application. You can see right away that two of the hooks we generated are being used. useRealm is being used to perform any write operations, and useQuery is used to access all the Tasks that have been created.

The application is providing a TextInput that will be used to generate a new Task. Once a Task is created, it will be displayed in the FlatList below. That timestamp we set up earlier is used to keep the list sorted so that the newest task is always at the top.

In order to keep this code short, we skipped a few best practices. All the methods provided to the application should ideally be set to variables and wrapped in a useCallback hook, so that they are not redefinined on every re-render. We are also using inline styles to spare a few more lines of code. One would normally generate a stylesheet using Stylesheet.create.

Here is the code for the application component:

function TaskApp() {
  const realm = useRealm();
  const tasks = useQuery(Task);
  const [newDescription, setNewDescription] = useState("")

  return (
    <SafeAreaView>
      <View style={{ flexDirection: 'row', justifyContent: 'center', margin: 10 }}>
        <TextInput
          value={newDescription}
          placeholder="Enter new task description"
          onChangeText={setNewDescription}
        />
        <Pressable
          onPress={() => {
            realm.write(() => {
              realm.create("Task", Task.generate(newDescription));
            });
            setNewDescription("")
          }}><Text>➕</Text></Pressable>
      </View>
      <FlatList data={tasks.sorted("createdAt")} keyExtractor={(item) => item._id.toHexString()} renderItem={({ item }) => {
        return (
          <View style={{ flexDirection: 'row', justifyContent: 'center', margin: 10 }}>
            <Pressable
              onPress={() =>
                realm.write(() => {
                  item.isComplete = !item.isComplete
                })
 }><Text>{item.isComplete ? "✅" : "☑️"}</Text></Pressable>
            <Text style={{ paddingHorizontal: 10 }} >{item.description}</Text>
            <Pressable
              onPress={() => {
                realm.write(() => {
                  realm.delete(item)
                })
              }} ><Text>{"🗑️"}</Text></Pressable>
          </View>
        );
      }} ></FlatList>
    </SafeAreaView >
  );
}

Todo list in 80 lines of code

Here is the example in full, including all the required import statements.

import React, { useState } from "react";
import { SafeAreaView, View, Text, TextInput, FlatList, Pressable } from "react-native";
import { Realm, createRealmContext } from '@realm/react'
class Task extends Realm.Object {
  _id!: Realm.BSON.ObjectId;
  description!: string;
  isComplete!: boolean;
  createdAt!: Date;

  static generate(description: string) {
    return {
      _id: new Realm.BSON.ObjectId(),
      description,
      createdAt: new Date(),
    };
  }

  static schema = {
    name: 'Task',
    primaryKey: '_id',
    properties: {
      _id: 'objectId',

description: 'string',
      isComplete: { type: 'bool', default: false },
      createdAt: 'date'
    },
  };
}

const { RealmProvider, useRealm, useQuery } = createRealmContext({ schema: [Task] })

export default function AppWrapper() {
  return (
    <RealmProvider><TaskApp /></RealmProvider>
  )
}

function TaskApp() {
  const realm = useRealm();
  const tasks = useQuery(Task);
  const [newDescription, setNewDescription] = useState("")

  return (
    <SafeAreaView>
      <View style={{ flexDirection: 'row', justifyContent: 'center', margin: 10 }}>
        <TextInput
          value={newDescription}
          placeholder="Enter new task description"
          onChangeText={setNewDescription}
        />
        <Pressable
          onPress={() => {
            realm.write(() => {
              realm.create("Task", Task.generate(newDescription));
            });
            setNewDescription("")
          }}><Text>➕</Text></Pressable>
      </View>
      <FlatList data={tasks.sorted("createdAt")} keyExtractor={(item) => item._id.toHexString()} renderItem={({ item }) => {
        return (
          <View style={{ flexDirection: 'row', justifyContent: 'center', margin: 10 }}>
            <Pressable
onPress={() =>
                realm.write(() => {
                  item.isComplete = !item.isComplete
                })
              }><Text>{item.isComplete ? "✅" : "☑️"}</Text></Pressable>
            <Text style={{ paddingHorizontal: 10 }} >{item.description}</Text>
            <Pressable
              onPress={() => {
                realm.write(() => {
                  realm.delete(item)
                })
              }} ><Text>{"🗑️"}</Text></Pressable>
          </View>
        );
      }} ></FlatList>
    </SafeAreaView >
  );
}

Tell me more

For more details on how to use @realm/react checkout our README and our documentation. If you are just getting started with React Native, you can also use our Expo templates to get started with minimal effort.

Let us know what you think

And with that being said, what do you think about @realm/react? Any other examples you would like to see? We are working hard to make it easy to integrate realm-js with react-native, so let us know if you have any questions or feature requests!

Happy Realming!

5 Likes

Wow, great development! I was waiting for a wrapper like this. To test it out it created a new Expo project based on the javascript Expo template you provided (not the TypeScript). The todo app runs flawlessly but once I enable the sync then it throws me a partitionValue must be of type ‘string’, ‘number’, ‘objectId’, or ‘null’ error. I tried several things to resolve it especially using the example code you provided for Native React. I got stuck. Any ideas what I do wrong here?

1 Like

Hi @Joost_Hazelzet, the partitionValue must be setup in Atlas.


If you want to enable Sync, you will need to set a partitionValue. This can be arbitrary, but it is usually an ID that the data can be filtered on (as in this example, the userID).
You can dynamically set the partitionValue in the RealmProvider component:

return (
    <RealmProvider sync={{user: loggedInUserObject, partitionValue: "someValue"}} ><TaskApp /></RealmProvider>
  )

The value should mirror the same type that you have setup in Atlas.
Glad you are enjoying the library! Let us know if this helps :slight_smile:

1 Like

Hey Andrew, yes it was the missing partitionValue in the RealmProvider and and now the Todo app is running like a charm including the tasks synched to the Atlas database. Thank you. :smiley:

My code is available as Github repository https://github.com/JoostHazelzet/expoRealm-js in case somebody wants to use it.

3 Likes

Can we setup multiple partition value in a single object?

Hi Andrew,

The app I’m trying to build currently needs to keep track of ordering for a set of items. My data is structured such that a Group has a Realm.List. I am using the useObject() hook to get the Group, and then I’m trying to render the Group.items in a React Native FlatList. However, I can’t use the Realm.List type as an argument to the data prop of FlatList. I have tried using the useQuery() hook to get that same list of Items, but I need to preserve ordering in the Group, so what I really need is access to the Group so I can add/remove items from Group.items.

Do you know of a way I can render a Realm.List?

Ah nevermind, I was able to get it working with react native’s VirtualizedList instead of using the FlatList which is more restrictive

You can create multiple RealmProviders with createRealmContext and have each using a different partition value. You will just have to export and use the hooks related to said partition.

2 Likes

I have written tests to do exactly what you have described. What exactly is the problem you are experiencing when trying to use a Realm.List in a FlatList? Feel free open an issue on github and provide some more information. I want to make sure that works :slight_smile:

2 Likes

I tried again and it works using a FlatList. I had just incorrectly specified an incorrect type for my parameter in my keyExtractor prop. I had specified the type of the variable as item: Item & Realm.Object instead of just Item. This was just a mistake on my part while I’m getting familiar with using Realm still! Thank you for the help!

2 Likes

Hi @Andrew_Meyer,

This is a great example. Do you have something similar for implementing authentication? The example from Mongo talks uses React and I’m having trouble adapting it. A React Native example would be much appreciated.

I’ll continue to search the forums to see if such an example already exists.

Thanks,
Josh