Observing Realm database transactions in React Native
Realm’s React Native SDK is an incredibly powerful tool that takes advantage of the NoSQL database developed by MongoDB. Realm can be used for offline first mobile apps or as a general purpose database for traditional mobile applications. One of the biggest advantages of using Realm for data storage is that it’s Objects are “live” by default; meaning that transactions and the state associated with a given Object can easily be observed by the rest of our application.
Though Realm Objects are “live”, they do not automatically trigger re-renders in our React Native components. One solution that I’ve found to be very helpful is to wrap Objects in custom React Hooks. Often times when folks see “custom hooks” they get nervous — this doesn’t have to be a scary process and is actually relatively straightforward in this context!
First we’ll need to define our schema so that we can have some data to interact with. We’re going to go for a super simple “Todo List” as to not make things too confusing. We’ll have a one-to-many relationship between our
TodoList and our
todos using embedded objects.
types.ts file will look like this:
schema.ts file will look like this:
And finally our
db.ts file will look like this:
Next we need to define a query so that we can interact with our data. Here, we will query for our
Now that we have our queries, we want to be able to observe the changes to our
Object. As we stated above, Realm
Objects will not automatically trigger state updates or re-render our components when they’re changed. The following component will not update to match the current state of our Realm
Object on update.
This will not work:
Enter our custom React hook:
What we’re doing here is enhancing the behavior of our Realm
Object by wrapping it in a custom hook that responds to events and manually updates state thus, triggering a re-render. Here, we are using the
addListener method on our Realm records, to listen for updates, and removing the listener in our effect cleanup with the
Another more extensible way of doing this is to abstract the meat of our
useTodoList hook into a helper function that can apply this behavior to all Realm records we want to listen on.
We’re now using a generic type and passing in our Realm Object — this will allow us to reuse the logic for any other Object schemas we might want to consume, reactively in our components.
Consuming our hook
Now that we’ve created our hooks, we can begin consuming them in our component. Our now reactive component will look like this:
Though Realm Objects aren’t reactive, they are “live” and this gives us the ability to augment them with reactive behavior. By building a set of hooks and a helper to enable us to build out hooks for each of our other Objects that may be added to our schema in the future, we are using common paradigms similar to that of Vuex, Apollo Client hooks and other stateful libraries.