21

I've picked realm to store data in my React Native app. I don't understand, how to organize files in my project. Documentation provides only simple code for one component. But I need for different components different parts of database.

I seen in one repository, where all schemes was passed to array in "configureRealm.js" file:

new Realm({schema: [Dogs, Cats]});

Also I've found, that I can put different schemes in "schemes" directory, for example and import them where I need.

For example in "Cats.js" react component I can do:

import Cats from 'schemes/Cats';

this.realm = new Realm({schema: [Cats]});

And in "Dogs.js" import dogs and initialize realm with this scheme.

But I am not sure in first and in mine way. What will be right and the best way to organize realm react native application?

acidernt
  • 2,173
  • 2
  • 19
  • 33
  • do you use redux? if so, make sure you are ok with performance issues, which are coming from realm+immutable pairing. – stkvtflw Oct 23 '16 at 16:35

2 Answers2

32

I recently began organizing my App/Data structure like this, when dealing with Realm, after getting some direction from someone much smarter than I :) I did not go into too much detail about how the Realms are initially created, as I assume you are handling that already. This is just a really solid structure for organization/compartmentalized development. Hope it helps!

.App
    ├──Assets
    ├──Data
    |   ├──Db
    |   |   ├──Db.js
    |   |   ├──DbHelper.js
    |   ├──Models
    |   |   ├──ModelA.js
    |   |   ├──ModelB.js
    |   |   ├──ModelC.js
    |   ├──Queries.js
    ├──Scenes
    |   ├──App.js
    |   |   ├──(all other scene/view components)

--The Models directory contains all my schemas, broken out individually like this:

import Realm from 'realm';
export default class ModelA extends Realm.Object {}
ModelA.schema = {
    name: 'ModelA',
    primaryKey: 'id',
    properties: {
        one: {type: 'int', optional: true},
        two: 'string',
        three: {type: 'string', optional: true},
    }
}

--In Db.js, I keep all my standard Realm related methods. createRealm(), write(), close(), insert(), and a generic query method, like this:

query(model: string, filter?: string) {
    let results = this.realm.objects(model);
    if(filter) {
        return results.filtered(filter);
    }
    return results;
}

--DbHelper.js then imports Db.js and all my Models. It handles the setting and getting of my db instance(s), using the standard methods from Db.js, like this:

import Db from 'App/Data/Db/Db';
import ModelA from 'App/Data/Models/ModelA';
import ModelB from 'App/Data/Models/ModelB';
import ModelC from 'App/Data/Models/ModelC';

class DbHelper {

    modelSchema = [
        ModelA,
        ModelB,
        ModelC
    ];

    activeInstancePath = (myLocalRealmPath)

    getInstance(): Db {
        let instance: Db = this.activeInstancePath;
        if(!instance) {
            throw new Error('DbHelper.js :: Active Instance Not Set!');
        }
        return instance;
    }

    /* note: this is where you would also setInstance and define a constant, or other method for the instance path */
}

--Queries.js then imports DbHelper.js. Queries.js contains all my methods for specific app related data queries. Queries.js is all I need to import into my Scene components, to obtain Realm data. My Queries.js looks something like this:

import DbHelper from 'App/Data/Db/DbHelper';

class Queries {

    /* a typical query */
    getSomeDataFromModelA(filterValue: string = null) {
        let filter = null;
        if (filterValue) {
            filter = `two = ${filterValue}`;
        }
        let results = DbHelper.getInstance()
            .query('ModelA', filter);

        return results;
    }

    /* return some JSON data that we originally stored in the Realm as a string */
    getSomeJsonData() {
        let results = DbHelper.getInstance()
            .query('ModelB');

        if(results.length) {
            let parsed = JSON.parse(results[0].objectA);
            return parsed.objectB;
        }
        return null;
    }
}
export default new Queries();

--App.js. So now, in my App Scene I would simply do something like this:

import React, { Component } from 'react';
import { View, Text } from 'react-native';
import Queries from 'App/Data/Queries';

class App extends Component {

    constructor(props) {
        super(props);

        // Get Some Realm Data!
        let modelAData = Queries.getSomeDataFromModelA()
        let someJsonData = Queries.getSomeJsonData();

        // Set Initial state
        this.state = {
            modelData: modelAData,
            jsonData: someJsonData
        }
    }

    componentDidMount() {
        console.log(this.state.modelData);
    }

    render() {
        return(
            <View>
                <Text>{this.state.jsonData.objectKey}</Text>
            </View>
        );
    }
}

export default App;
fostertime
  • 565
  • 5
  • 12
  • Interesting. I'm looking to do something like this but it's gotten even more complicated because when you're syncing with the Realm Object Server, the Realm instance is asynchronous and returned as a Promise, so you need to have Promises "all the way down". – Joshua Pinter Feb 10 '17 at 18:48
  • Also, quick question: why import both `Db` and `DbHelper` in `Queries`? `DbHelper` already imports `Db`? Thanks. – Joshua Pinter Feb 10 '17 at 18:49
  • Yes, you are correct. You don't need to import Db.js into Queries.js. I think in that specific project there was a certain need for that, but can't remember what it was now. ;) In regards to Promises, I've found using async helps simplify that whole process. You could just make every method an `async` method and `await` each subsequent method call, wrapping them in a simple try/catch block. – fostertime Mar 01 '17 at 16:29
  • You're definitely right with the `async` and `await` method. It takes a little bit to wrap your head around but then it makes things easy peezie. Thanks. – Joshua Pinter Mar 01 '17 at 22:20
  • Thanks for the great answer. For newbie's like me it might be beneficial to also show how you export your Queries. I would assume it would be something like `export default new Queries` ? – Marklar Mar 13 '17 at 05:57
  • 1
    @Marklar yes, you would want Queries to be a singleton. So you'd export it like this: `export default new Queries();` I will update the answer to include that line. – fostertime Mar 13 '17 at 15:29
  • @fostertime are you using [multiple realms](https://realm.io/docs/javascript/latest/#multiple-realms)? I'm struggling a little to follow the `getInstance(): Db` example. – Marklar Mar 15 '17 at 01:36
  • 1
    @Marklar yes, this project utilized multiple realms. I typically use at least two realms for a project. One for App Data, another for App Settings that I want to distribute with the app and also use for global settings, like an `appInitialized` status. If you do not want multiple Realms, just change `getInstance` to `getRealm` or something like that. And don't worry about setting any `activeInstancePath`, since you'd only ever have one path. – fostertime Mar 16 '17 at 15:40
  • 3
    @fostertime - would you guys mind sharing a snippet of code for opening a remote realm? How are you accessing the data? I have a redux store and actually tried to go around the whole asynchronous stuff by logging the user, saving the user object in my redux store and just passing that user to the getInstance function, but this doesn't seem to work & I have to init the realm inside of a login operation. Unfortunately I can't wrap my head around that. – Daniel Dimitrov Jul 17 '17 at 11:55
  • Very interesting! Thx :) – Yonedev Dec 21 '17 at 13:59
  • @fostertime have you updated your code base to use the async `open` and `openAsync` methods to open the Realm? – Marklar Mar 13 '18 at 01:48
  • @fostertime can you add Db.js here or any sample project in github with this structure. – YLS Jun 07 '18 at 10:13
  • @Marklar I will be updating all of this to reflect the latest versions of Realm/React Native, etc. Just haven't had the time... let me know if there are any specifics I could help with in the mean time. – fostertime Jun 10 '18 at 17:15
  • @YLS see my previous comment. Let me know if there is a specific thing I could help with, until I get all this updated. – fostertime Jun 10 '18 at 17:16
  • @fostertime, thanks, really looking forward to the update. The specifics for me would be how to use the async `open` and `openAsync` methods to open your realm and then `export / import` the realm into other screens of my react native app. – Marklar Jun 11 '18 at 10:54
  • @Marklar : I'm not clear about DbHelper.js and Db.js class here , can you share some detailed snippet for the same. – Dharmita bhatt Jan 02 '19 at 17:19
  • @Dharmitabhatt have a look at the discussion in the [realm js github repo](https://github.com/realm/realm-js/issues/1320) – Marklar Jan 07 '19 at 22:56
1

In the example in the Realm github repo all models are defined and exported form a singe file: https://github.com/realm/realm-js/blob/master/examples/ReactExample/components/realm.js

Then this is required throughout the app and used as needed.

Ari
  • 1,439
  • 10
  • 7