In this 55-minute recording, Andrew spends about 45 minutes presenting
- React Native Overview & Benefits
- React Native Key Concepts and Architecture
- Realm Integration with React Native
- Realm Best Practices / Tips&Tricks with React Native
After this, we have about 10 minutes of live Q&A with Ian & Andrew and our community . For those of you who prefer to read, below we have a full transcript of the meetup too.
Throughout 2021, our Realm Global User Group will be planning many more online events to help developers experience how Realm makes data stunningly easy to work with. So you don't miss out in the future, join our Realm Global Community and you can keep updated with everything we have going on with events, hackathons, office hours, and (virtual) meetups. Stay tuned to find out more in the coming weeks and months.
To learn more, ask questions, leave feedback, or simply connect with other Realm developers, visit our community forums. Come to learn. Stay to connect.
(As this is verbatim, please excuse any typos or punctuation errors!)
Ian: I'm Ian Ward. I'm a product manager that focuses on the Realm SDKs. And with me today, I'm joined by Andrew Meyer, who is an engineer on our React Native development team, and who is focusing on a lot of the improvements we're looking to make for the React Native SDK in the future. And so I just went through a few slides here to just kick it off. So we've been running these user group sessions for a while now, we have some upcoming meetups next week we are going to be joined by a AWS engineer to talk about how to integrate MongoDB Realm, our serverless platform with AWS EventBridge. A couple of weeks after that, we will also be joined by the Swift team to talk about some of the new improvements they've made to the SDK and developer experience. So that's key path filtering as well as automatic open for Realms.
We also have MongoDB.live, which is happening on July 13th and 14th. This is a free virtual event and we will have a whole track set up for Realm and mobile development. So if you are interested in mobile development, which I presume you are, if you're here, you can sign up for that. No knowledge or experience with MongoDB is necessary to learn something from some of these sessions that we're going to have.
A little bit of housekeeping here. So we're using this Bevy platform. You'll see, in the web view here that there's a chat. If you have questions during the program, feel free to type in the question right there, and we'll look to answer it if we can in the chat, as well as we're going to have after Andrew goes through his presentation, we're going to have a Q&A session. So we'll go through some of those questions that have been accumulating across the presentation. And then at the end you can also ask other questions as well. We'll go through each one of those as well. If you'd like to get more connected we have our developer hub. This is our developer blog, We post a bunch of developer focused articles there. Please check that out at developer.mongodb.com, many of them are mobile focus.
So if you have questions on Swift UI, if you have questions on Kotlin multi-platform we have articles for you. If you have a question yourself come to forums.realm.io and ask a question, we patrol that regularly and answer a lot of those questions. And of course our Twitter @realm please follow us. And if you're interested in getting Swag please tweet about us. Let us know your comments, thoughts, especially about this program that you're watching right now. We would love to give away Swag and we'd love to see the community talk about us in the Twitter sphere. And without further ado, I'll stop sharing my screen here and pass it over to Andrew. Andrew, floor's yours.
Let's get started. So my agenda today, I'm going to go over React Native. I'm also going to go over some key concepts in React. We're going to go over how to integrate Realm with React Native, some best practices and tips when using Realm with React Native. And I'm also going to go over some upcoming changes to our API. So what is React Native? I think we've got a pretty mixed group. I'm not sure how many of you are actually React Native developers now or not. But I'm going to just assume that you don't know what React Native is and I'm going to give you a quick overview. So React Native is a framework made from Facebook. It's a cross platform app development library, you can basically use it for developing both Android and iOS applications, but it doesn't end there; there is also the ability to make desktop applications with React Native windows and React Native Mac OS.
Another cool feature about React Native is fast refresh. This was implemented a few years ago, basically, as you develop your code, you can see the changes real time in your simulator, actually on your hardware as well. It can actually handle multiple simulators and hardware at the same time. I've tested Android, iOS phones in multiple languages and sizes and was able to see my front end changes happen in real time. So that's super useful if you've ever done a native development in iOS or an Android, you have to compile your changes and that takes quite a bit of time.
So this is an example of some React code. This is basically just a small piece of text to the button that allows you to change it from lowercase to uppercase. This is an example of a class component. There's actually two ways that you can make components in React, class components and functional components. So this is an example of how you do it with a class component, basically you make an instructor where you set your initial state then you have a rendering function that returns JSX. So this JSX reacts on that state. So in this case, we have a toUpper state with just a Boolean. If I change this toUpper Boolean to true, then that's going to change the text property that was passed in to uppercase or lowercase. And that'll be displayed here in the text. To set that state, I call this dot set state and basically just toggle that Boolean from true to false or false to true, depending on what state it's in.
So, as I said, this is class components. There's a lot more to this. Basically there's some of these life cycle methods that you had to override. You could basically before your component is mounted, make a network request and maybe initiate your state with some of that data. Or if you need to talk to a database that's where you would handle that. There's also a lot of... Yeah, the lifecycle methods get pretty confusing and that's why I'm going to move on to functional components, which are quite simpler. Before we could use this, but I think three years ago React introduced hooks, which is a way that we can do state management with functional programming. This gets rid of all those life cycle methods that are a bit confusing to know what's happening when.
So this is an example of what a functional component looks like. It's a lot less code, your state is actually being handled by a function and this function is called useState. Basically, you initialize it with some state and you get back that state and a function to set that state with. So in this case, I can look at that toUpper Boolean here and call this function to change that state. I want to go back real quick, that's how it was looking before, and that's how it is now. So I'm just going to go quickly through some of the hooks that are available to you because these are pretty much the basics of what you need to work with React and React Native. So as I talked about useState before this is just an example of showing a modal, but it's not too different than changing the case of a text.
So basically you'd be able to press a button and say, show this modal, you pass that in as a property to your modal. And you could actually pass that set modal, visible function to your modal components so that something inside of that modal can close that. And if you don't know what the modal is, it's basically an overlay that shows up on top of your app.
So then the next one is called useEffect. This is basically going to replace all your life cycle methods that I talked about before. And what you can do with useEffect is basically subscribe to changes that are happening. So that could be either in the state or some properties that are being passed down. There's an array at the end that you provide with the dependencies and every time something changes this function will be called. In this case, it's just an empty array, which basically means call this once and never call it again. This would be if you need to initialize your state with some data that's stored in this case in persistent storage then you'd be able to get that data out and store it to your state. We're going to see a lot more of this in the next slides.
UseContext is super useful. It's a bit confusing, but this is showing how to use basically a provider pattern to apply a darker light mode to your application. So basically you would define the styles that you want to apply for your component. You create your context with the default state and that create gives you a context that you can call the provider on, and then you can set that value. So this one's basically overriding that light with dark, but maybe you have some sort of functionality or a switch that would change this value of state and change it on the fly. And then if you wrap your component or your entire application with this provider, then you can use the useContext hook to basically get that value out.
So this could be a very complex app tree and some button way deep down in that whole tree structure that can just easily get this theme value out and say, "Okay, what am I, dark or light?" Also, you can define your own hooks. So if you notice that one of your components is getting super complex or that you created a use effect that you're just using all over the place, then that's probably a good chance for you to do a little bit of dry coding and create your own hooks. So this one is basically one that will check a friend status. If you have some sort of chat API, so you'd be able to subscribe to any changes to that. And for trends it's Boolean from that to let you know that friends online or not. There's also a cool thing about useEffect. It has a tear down function. So if that component that's using this hook is removed from the tree, this function will be called so that those subscription handlers are not going to be called later on.
A couple of other hooks useCallback and useMemo. These are a bit nice, this is basically the concept of memorization. So if you have a component that's doing some sort of calculation like averaging an array of items and maybe they raise 5,000 items long. If you just call the function to do that in your component, then every time your component got re-rendered from a state change, then it would do that computation again, and every single time it got re-rendered. You actually only want to do that if something in that array changes. So basically if you use useMemo you can provide dependencies and then compute that expensive value. Basically it helps you not have an on performance app.
UseCallback is similar, but this is on return to function, this function will basically... Well, this is important because if you were to give a function to a component as a property, and you didn't call useCallback to do that, then every time that function re-rendered any component that was using that function as a property would also be re-rendered. So this basically keeps that function reference static, basically makes sure it doesn't change all the time. We're going to go into that a little bit more on the next slides. UseRef is also quite useful in React Native. React Native has to sometimes have some components that take advantage of data features on your device, for instance, the camera. So you have a camera component that you're using typically there might be some functions you want to call on that component. And maybe you're not actually using properties to define that, but you actually have functions that you can call in this case, maybe something that turns the flashlight on.
In that case, you would basically define your reference using useRef and you would be able to basically get a reference from useRef and you can the useRef property on this component to get a reference of that. Sorry, it's a bit confusing. But if you click this button, then you'd be able to basically call function on that reference. Cool. And these are the rest of the hooks. I didn't want to go into detail on them, but there are other ones out there. I encourage you to take a look at them yourselves and see what's useful but the ones that I went through are probably the most used, you get actually really far with useState, useEffect, useContext.
And you can either use object data sign, but basically the best method right now is to use the spread operator and what this does is basically take all the properties of that object and makes a copy of them here and then overrides that message with the texts that you entered into this text input. Cool. And back to that concept of memorization. There's actually a pretty cool function from React to that, it's very useful. When we had class components, there used to be a function you could override that compared the properties of what was changing inside of your component. And then you would be able to basically compare your previous properties with your next properties and decide, should I re-render this or not, should I return false, it's going to re-render. If you return true, then it's just going to stay the same.
To do that with functional components, we get a function called the React.memo. Basically, if you wrap your component React.memo it's going to automatically look at those base level properties and just check if they're equal to each other. With objects that becomes a little bit problematic, if it's just strings and Booleans, then it's going to successfully pull that off and only re-render that component if that string changes or that Boolean changes. So if you do wrap this and you're using objects, then you can actually make a function called... Well, in this case, it's equal or are equal, which is the second argument of this memo function. And that will give you the access to previous prompts and next prompts. So if you're coming into the hooks world and you already have a class component, this is a way to basically get that functionality back. Otherwise hooks is just going to re-render all the time.
So let's see if there's any questions at the moment, nothing. Okay, cool. So that brings us to Realm. Basically, how do you persist state? We have our hooks, like useState that's all great, but you need a way to be able to save that state and persist it when you close your app and open it again. And that's where Realm comes in. Realm has been around for 10 years, basically started as an iOS library and has moved on to Native Android and .NET and React Native finally. It's a very fast database it's actually written in C++, so that's why it's easily cross-platform. Its offline first, so most data that you usually have in an application is probably going to be talking to a server and getting that data off that, you can actually just store the data right on the phone.
This is a shortcut, if anybody wanted to know how to install your pods without jumping into the iOS directory, if you're just getting to React Native, you'll know what I'm talking about later. So there's a little bit of an introduction around, so basically if you want to get started using Realm, you need to start modeling your data. Realm has schemas to do that, basically any model you have needs to have a schema. You provide a name for that schema, you define properties for this. Properties are typically defined with just a string to picking their type. This also accepts an object with a few other properties. This would be, if we were using the objects INTAX, then this would be type colon object ID, and then you could also make this the primary key if you wanted to, or provide some sort of default value.
We also have a bit of TypeScript support. So here's an example of how you would define a class using this syntax to basically make sure that you have that TypeScript support and whatever you get back from your Realm queries is going to be properly typed. Basically, so this is example of a journal from my previous schema definition here. And what's important to notice is that you have to add this exclamation point, basically, this is just telling TypeScript that something else is going to be defining how these properties are being set, which Realm is going to be doing for you. It's important to know that Realm objects, their properties are actually pointers to a memory address. So those will be automatically propagated as soon as you connect this to Realm.
In this example, I created a generate function. This is basically just a nice syntax, where if you wanted to basically define an object that you can use to create Realm objects you can do that here and basically provide some values, you'll see what I mean in a second how that works. So once you have your schema defined then you can put that into a configuration and open the Realm, and then you get this Realm object. When you create a Realm, then it's going to actually create that database on your phone. If you close it, then it'll just make sure that that's saved and everything's good to go. So I'm going to show you some tips on how to keep that open and close using hooks here in a second.
Another thing that's pretty useful though, is when you're starting to get getting started with defining your definitions, your schema definitions, you're getting started with your app, it's pretty useful to put this deleteRealmMigrationNeeded to true. Basically that's if you're adding new properties to your Realm in development, and it's going to yell at you because it needs to have a migration path. If you've put this to true, then it's just going to ignore that, it's going to delete all that data and start from scratch. So this is pretty useful to have in development when you're constantly tweaking changes and all that to your data models.
If you want to change anything, say that display journal in this case, it's just the journal that I'm working on in some component, then if I wrap this in a right transaction, I can immediately manipulate that that property and it'll automatically be written to the database. I'll show you how to manage state with that in a second because it's a bit tricky. And then if you want to delete something, then basically you just provide what's coming back from realm.object creation into this, or realm.query into this delete function and then it'll remove that from the database. In this example, I'm just grabbing a journal by the ID primary key.
And last but not least how to read data. There's two main functions I basically use to get data out of Realm, one is using realm.objects. Oops, I have a little bit of code there. If you call realm.objects and journal and forget about this filtered part basically it'll just get everything in the database for that model that you defined. If you want to filter it by something, say if you have an author field and it's got a name, then you could say, I just want everything that was authored by Andrew then this filter would basically return a model that's filtered and then you can also sort it. But you can chain these as well as you see, you can just be filtered or realm.object.filtered.sorted, that'd be the better syntax, but for readability sake, I kept it on one line. And if you want to get a single object, you can use object for primary and provide that ID.
So I'm going to go through a few best practices and tips to basically combine this knowledge of Realm and hooks, it's a lot, so bear with me. So if you have an app and you need to access Realm you could either use a singleton or something to provide that, but I prefer to make sure to just provide it once and I found that using useContext is the best way to do that. So if you wanted to do that, you could write your own Realm provider, basically this is a component, it's going to be wrapping. So if you make any sort of component, that's wrapping other components, you have to give children and you have to access the children property and make sure that what you're returning is implementing those children otherwise you won't have an app it'll just stop here. So this Realm provider is going to have children and it's going to have a configuration just like where you defined in the previous slide.
And basically I have a useEffect that basically detects changes on the configuration and opens the Realm and then it sets that Realm to state and adds that to that provider value. And then if you do that, you'll be able to use that useContext to retrieve that realm at any point in your app or any component. So if you wrap that component with Realm provider, then you'll be able to get that Realm. I would recommend making a hook for this called useRealm or something similar where you can have error checking and any sort of extra logic that you need when you're accessing that Realm here and have that return that context for you to use.
So another thing, initializing data. So if you have a component and it's the very first time your app is opened you might want to initialize it with some data. The way I recommend doing that is making an effect for it, basically calling realm.objects and setting that to your state, having this useEffect listen for that state and just check, do we have any entries? If we don't have any entries then I would initialize some data and then set that journal up. So going on the next slide. And another very important thing is subscribing to changes. Yeah, basically if you are making changes to your collection in Realm, it's not going to automatically re-render. So I recommend using useState to do that and keeping a copy of that realm.object in state and updating with set state. And basically all you need to do is create an effect with a handle change function. This handle change function can be given to these listeners and basically it will be called any time any change happens to that Realm collection.
You want to make sure though that you do check if there are any modifications before you start setting state especially if you're subscribing to changes to that collection, because you could find yourself into an infinite loop. Because as soon as you call ad listener, there will be an initial event that fires and the length of all the changes is zero. So this is pretty important, make sure you check that there actually are changes before you set that state. So here's an example of basically providing or using a FlatList to display around data. FlatList is one of the main components from React Native that I've used to basically display any list of data. FlatList basically takes an array of data, in our case, it'll also take a Realm collection, which is almost an array. It works like an array. So it works in this case. So you can provide that collection.
I recommend sorting it because one thing about Realm collections is the order is not guaranteed. So you should sort it by some sort of timestamp or something to make sure that when you add new entries, it's not just showing up in some random spot in the list. It's just showing up in this case at the creation date. And then it's also recommended to use a key extractor and do not set it to the index of the array. That's a bad idea, set it to something that is that's unique. In this case, the idea that we were using for our Realm is object ID, in the future we'll have a UUID property coming out, but in the meantime, object ID is our best option for providing that for you to have basically a unique ID that you can define your data with. And if you use that, I recommend using that. You can call it the two check string function on here because key extractor wants a string. He's not going to work with an object. And then basically this will make sure that your items are properly rendered and not rerunning the whole list all the time.
Also, using React.memo is going to help with that as well, which I'm going to show you how to do that. This item in this case is actually a React.memo. I recommend instead of just passing that whole item as a property to maybe just get what you need out of it and passing that down and that way you'll avoid any necessary re-renders. I did intentionally put a mistake in here. ID is an object, so you will have to be careful if you do it like this and I'll show you how that's done. you could just set it to string and then you wouldn't have to provide this extra function that on purpose I decided to put the object and to basically show you how you can check the properties and, and update this. So this is using React.memo and basically it will only render once. It will only render if that title changes or if that ID changes, which it shouldn't change.
Basically, this guy will look at is title different? Are the IDs different? If they're not return true, if any of them changed return false. And that'll basically cause a re-render. So I wrote quite a bit of sample code to basically make these slides, if you want to check that out, my GitHub is Takameyer, T-A-K-A-M-E-Y-E-R. And I have a Realm and React Native example there. You can take a look there and I'll try to keep that updated with some best practices and things, but a lot of the sample code came from there. So I recommend checking that out. So that's basically my overview on React and Realm. I'll just want to take an opportunity to show up what's coming up for these upcoming features. Yeah, you just saw there was quite a lot of boiler plate in setting up those providers and schemas and things.
So basically saying, "Hey, this description is a type string, or this ID is primary key and type object ID." My goal eventually when TypeScript supports it, I would like to infer the types from the TypeScript types that you're defining here. So at the moment we're probably going to have to live with just defining it twice, but at least they're not too far from each other and you can immediately see if they're not lining up. I didn't go over relations, but you can set up relations between Realms models. And that's what I'm going to revive with this link from property, this is bit easier, send texts, get that done. You can take a look at our documentation to see how you do that with normal schemas. But basically this is saying I'm linking lists from todoLists because a TodoItem on the items property from todoList link from todoList items, reads kind of nice.
Yeah, so those are basically how we're going to define schemas in the future. And we're also going to provide some mutator functions for your business logic in your classes. So basically if you define the mutator, it'll basically wrap this in a right transaction for you. So I'm running out of time, so I'm just going to go for the next things quick. We have Realm context generator. This is basically going to do that whole provider pattern for you. You call createRealmContext, give it your schemas, he's going to give you a context object back, you can call provider on that, but you can also use that thing to get hooks. I'm going to provide some hooks, so you don't have to do any sort of notification handling or anything like that. You basically call the hook on that context. You give it that Realm class.
And in this case use object, he's just going to be looking at the primary key. You'll get that object back and you'll be able to render that and display it and subscribe to updates. UseQuery is also similar. That'll provide a sorting and filter function for you as well. And that's how you'd be able to get lists of items and display that. And then obviously you can just call, useRealm to get your Realm and then you can do all your right transactions. So that's coming up and that's it for me. Any questions?
Ian: Yeah. Great. Well, thank you, Andrew. We don't have too many questions, but we'll go through the ones we have. So there's one question around the deleteRealmIfMigrationNeeded and the user said this should only be used in dev. And I think, yes, we would agree with that, that this is for iterating your schema while you're developing your application. Is that correct Andrew?
Andrew: Yeah, definitely. You don't want to be using that in production at all. That's just for development. So Yeah.
Andrew: Yeah. If you're using basically that syntax I showed to you, you should still see the filtered function on all your collections. If you are looking at using filtered in that string, we don't have any sort of static analysis for those query strings yet, but definitely for the future, we could look at that in the future.
Okay, great. What is your opinion benefit, would you say makes a difference better to use than the likes of PouchDB? So certainly noted here that this is a SDK for React Native. We're depending on the hard drive to be available in a mobile application. So for PouchDB, it's more used in web browsers. This SDK you can't use it in a web browser. We do have a Realm web SDK that is specific for querying data from Atlas, and it gives some convenience methods for logging into our serverless platform. But I will say that we are doing a spike right now to allow for compilation of our Realm core database into. And if we do that, we'll be able to then integrate into browsers and have the ability to store and persist data into IndexedDB, which is a browser available or is the database available in browsers. Right. And so you can look forward to that because then we could then be integrated into PWAs for instance in the web.
Other Question here, is there integration, any suggestions talk around Realm sync? Is there any other, I guess, tips and tricks that we can suggest the things may be coming in the future API regarding a React Native application for Realm sync? I know one of the things that came out in our user interviews was partitions. And being able to open up multiple Realms in a React Native SDK, I believe we were looking to potentially add this to our provider pattern, to put in multiple partition key values. Maybe you can talk a little bit to that.
Andrew: Yeah. Basically that provider you'd be able to actually provide that configuration as properties as well to the provider. So if you initiate your context with the configuration and something needs to change along the line based on some sort of state, or maybe you open a new screen and it's like a detailed view. And that parameter, that new screen is taking an ID, then you'd be able to basically set the partition to that ID and base the data off that partition ID.
Ian: Yeah, mostly it's our recommendation here to follow a singleton pattern where you put everything in the provider and that when you call that in a new view, it basically gives you an already open Realm reference. So you can boot up the app, you open up all the rounds that you need to, and then depending on the view you're on, you can call to that provider to get the Realm reference that you'd need.
Andrew: Right. Yeah. That's another way to do it as well. So you can do it as granular as you want. And so you can use your provider on a small component on your header of your app, or you could wrap the whole app with it. So many use cases. So I would like to go a little bit more into detail someday about how to like use Realm with React navigation and multiple partitions and Realms and stuff like that. So maybe that's something we could look at in the future.
Ian: Yeah. Absolutely. Great. Are there any other questions from anyone here? Just to let everyone know this will be recorded, so we've recorded this and then we'll post this on YouTube later, so you can watch it from there, but if there's any other questions, please ask them now, otherwise we'll close early. Are there any issues with multiple independently installed applications accessing the same database? So I think it's important to note here that with Realm, we do allow multi-process access. We do have a way, we have like a lot file and so there is the ability to have Realm database be used and access by multiple applications if you are using a non-sync Realm. With sync Realms, we don't have multi-process support, it is something we'll look to add in the future, but for right now we don't have it. And that's just from the fact that our synchronization runs in a background thread. And it's hard for us to tell when that thread has done to at work or not.
Another question is the concept behind partitions. We didn't cover this. I'd certainly encourage you to go to firstname.lastname@example.org/realm we have a bunch of documentation around our sync but a partition corresponds to the Realm file on the client side. So what you can do with the Realm SDK is if you enable sync, you're now sinking into a MongoDB Atlas server or cluster. This is the database as a service managed offering that is for the cloud version of MongoDB. And you can have multiple collections within this MongoDB instance. And you could have, let's say a hundred thousand documents. Those a hundred thousand documents are for the amalgamation of all of your Realm clients. And so a partition allows you to specify which documents are for which clients. So you can boot up and say, "Okay, I logged in. My user ID is Ian Ward. Therefore give me all documents that are for Ian Ward." And that's where you can segment your data that's all stored together in MongoDB. Interesting Question.
Andrew: Yeah. I feel like a simple application, it's probably just going to be partitioned by a user ID, but if you're making an app for a logistics company that has multiple warehouses and you have an app that has the inventory for all those warehouses, then you might probably want to partition on those warehouses, the warehouse that you're in. So that'd be a good example of where you could use that partition in a more complex environment.
Ian: Yeah, definitely. Yeah. It doesn't need to be user ID. It could also be store ID. We have a lot of logistics customer, so it could be driver ID, whatever packages that driver is supposed to deliver on that day will be part of their partition. Great. Well if there's no other... Oops, got another one in, can we set up our own sync on existing Realm database and have it sync existing data i.e. the user used the app without syncing, but later decides to sync the data after signing up? So right now the file format for a Realm database using non-sync and a syncing database is different basically because with sync, we need to keep track of the operations that are happening when you're occurring offline. So it keeps a queue of those operations.
Ian: And then once you connect back online, it automatically sends those operations to the service side to apply the state. Right now, if you wanted to move to a synchronized brown, you would need to copy that data from the non-sync Realm to the sync Realm. We do have a project that I hope to get to in the next quarter for automatically doing that conversion for you. So you'll basically be able to write all the data and copy it over to the sync and make it a lot easier for developers to do that if they wish to. But it is something that we get some requests for. So we would like to make it easier. Okay. Well, thank you very much, everyone. Thank you, Andrew. I really appreciate it. And thank you everyone for coming. I hope you found this valuable and please reach out to us if you have any further questions. Okay. Thanks everyone. Bye.