A topic that has come up numerous times in consulting calls, among students in my course, and on is offline support. In short, how can I do make my React Native + Meteor app offline capable? Learn React Native + Meteor issues for react-native-meteor I’ve thought about this before but never came to a great conclusion — especially for data that is coming in from a publication. The solutions I always came up with before relied on the user having to set up Redux in their app. Through various conversations I’ve found that many people using Redux in their React Native + Meteor app, which is perfectly fine. Minimongo does many of the same things as Redux and it’s what Meteor developers are used to. aren’t The Problem There are many problems associated with building on offline first app — saving data to the client so it works offline, reconciling actions/methods taken when offline, and minimizing any duplicate subscription data. The Goal With this post I’m going to try to accomplish saving data, that was brought in via a Meteor subscription, on the client so that it’s there the next time the user starts the app. It’s primary goal is to give a user a useful experience as soon as they open the app without having to wait on a subscription to complete or rely on an internet connection. Data should stay on the device until a message has been received to remove that document. The Strategy I could just publish a package and tell you to use it but I want think this is a solution that will take some time to develop into something truly usable that covers multiple use cases. So in this article we’ll be covering how to implement the logic in your own app so that you can use it, understand it, experiment with it, and provide feedback. Before diving into the implementation I have to give credit where credit is due and in this case it’s to who developed a package, , which I used as a starting point for much of what I’m going to cover. His solution works well if you’re already using Redux in your app but I wanted to shoot for a more simple implementation. Julian_Kingman react-native-meteor-redux To accomplish this we’ll listen to three ddp messages — , , and . We’ll also be implementing redux behind the scenes so that we can use the excellent library to actually handle saving data to disk as well as the ability to automatically “rehydrate” data. added changed removed redux-persist Implementation The app we’re building will be extremely simple. The Meteor app will simply be the result of running . We’ll then be able to use the web interface created there to modify data. On the react native side I’ve created a very simple app, with the help of that lists the items and then allows us to add new links. To keep this tutorial focused the “new” links will be randomly chosen from an array links. meteor create --full MeteorApp react-native-elements We’ll also going to display the app’s DDP connection status, which signifies if we’re connected to the Meteor server or not. You can create the basic app by following these steps. react-native init RNDemonpm i --save lodash redux react-native-elements react-native-meteor react-native-vector-icons redux redux-logger redux-persistreact-native link react-native-vector-icons Then copy and paste the to your app entry point. You should now be able to follow along with this tutorial. A full Github link to the demo is available at the end of this post. contents of this file First thing we want to do is setup our public API for this offline functionality. I’ll be doing my work in and exporting an function which will be called directly after I call . react-native-meteor-offline.js initializeMeteorOffline Meteor.connect That’s all the work we’ve got to do in our existing Meteor app. Now inside of our function we want to listen to a few DDP messages that pertain to subscription responses. makes that easy for us by exposing the DDP connection at , which we can then listen for events on. initializeMeteorOffline react-native-meteor Meteor.ddp If you have a meteor app running at you may see a flood of messages in your console at this point. This is the information we’ll use to save data, via redux, to disk so that the user may have a usable offline experience. localhost:3000 The first step to persisting this to set up the Redux store. I won’t go into the details of Redux but our store is where we “store” all of our data — tricky naming, huh? We’ll also be leveraging a convenience package, , which will help give us insight into what is happening with Redux. redux-logger With the store configured we can then start dispatching actions upon any of these three DDP messages. This will allow the reducer to response correctly depending on the type of message, which will then update data on disk. Now we need to use the reducer to actually update our store when certain actions are dispatched. This will be the bulk of our code and all it’s doing is gradually building up an object with all of our data, modifying any pieces if necessary, and finally removing any data when told to by the server. If you spot any errors in this please let me know. We’re then able to check, via , that our redux store is being updated correctly. redux-logger The last piece we need to accomplish is actually persisting the data that we’re writing to Redux to disk, via AsyncStorage, and then to populate our minimongo-cache with that data upon app startup. makes this very easy for us to do. redux-persist First we have to tell what store we want to save to disk and tell it a storage engine (in our case AsyncStorage). We’ll also be leveraging a few other options to improve performance and interoperability. We’ll improve performance by “debouncing” how often we write to disk, which is an expensive operation, meaning that the write-to-disk will happen only every X milliseconds (1000 by default). We’ll also attempt to improve interoperability by setting a unique key to save data in AsyncStorage. That way, if you’re using in your app already data won’t be overridden between them. redux-persist redux-persist We then want to tell Redux to automatically rehydrate our store with the information that was saved on disk when it’s first initialized. This will be beneficial because we can read from disk faster than we can from over the network and we ensure that the last dataset that existed when the user last used the app is still on the device. If you look at your console log now you should see a new action dispatched and it should be the first one you see. This is pulling data from the disk and then populating the Redux store from it, all before we’ve connected to the DDP server. But we’re not quite done yet. Though our Redux store has our cached data the actual Meteor app does not. For this we’ll need to insert data to minimongo-cache upon successful rehydration. This goes through all of the data we have in the store and then inserts it into the minimongo-cache, which is what our Meteor app uses. You can then use just like you would normally, even offline. Meteor.collection('links').find() . The full file is available on Github Testing Videos are easier than words at times. I’ve recorded a short video that demonstrates the capabilities of this code. Example app on Github Interested in learning more about React Native + Meteor? and I can help! Sign up for my email list
Share Your Thoughts