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
.
@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!