Use mobx or redux or with repository pattern and persistent local storage realm or sqlite?

766

Solution 1

Indeed you can use repository pattern.

On your repository, you may have a save method.

    save(group: GroupLocalStorageModel): Promise<boolean> {
        let created;
        this._localStorage.write(() => {
            created = this._localStorage.create<GroupLocalStorageModel>("Group", group);
        });
        return Promise.resolve(true);
    }

This method will literally save your entity to some local storage you set. In the example above, we are saving a group object to a Group collection which are like tables. We are using realm which is no-sql.

Once you have your repository, if you are using either redux or mobx, you will probably call your save method on your action. Both redux and mobx work with actions, right?

export const GroupStoreModel = types
    .model("GroupStore")
    .props({
        groups: types.optional(types.array(GroupModel), []),
    })
    .extend(withEnvironment)
    .actions((self) => {
        return ({
            _addGroupToStore(group: GroupLocalStorageModel) {
                self.groups.push(group)
            },
            _deleteAllFromStore() {
                self.groups.clear()
            },
            _addGroupsToStoreBatch: (groups: GroupLocalStorageModel[]) => {
                self.groups.concat(groups);
            },
        })
    })
    /* Async actions */
    .actions((self) => {
        let groupRepository = self.environment.groupRepository;
        return ({
            addGroup(group: GroupLocalStorageModel) {
                groupRepository.save(group).then(result => self._addGroupToStore(group))
            },
            getAllGroupsPaginated(page: number) {
                groupRepository.getAllPaginated(page).then(groups => self._addGroupsToStoreBatch(groups));
            },
            deleteAll() {
                groupRepository.deleteAll();
                self._deleteAllFromStore();
            }
        })
    })

In this example, we are using mobx-state-tree. And this addGroup action will update firstly our database, and then update also the global state.

We still want to use our global state so our views will be re-rendered automatically according to either connect for redux or observable for mobx.

See more informations here on the repository:

https://github.com/Hadajung/poc-react-native-database-example

Solution 2

I'm not really sure what you need but if I understood you correctly you need to persist large amounts of data, and also to load that same data, but only in batches.

I believe that this kind of problem can be solved with a repository pattern and SOLID design principles.

You will need:

  • store class (mobx store) that holds your business logic.

  • repository class which is responsible for retrieving and persisting data.

The store gets the repository injected into it via the constructor. Then when you call the initialize method on your store, it talks to the repository and retrieves the initial data. Now, initial data can be only a subset of all the data that is persisted. And you can implement some kind of paging on the store and repository, to retrieve data in batches as needed. Later you can call other methods to load and save additional data as needed.

pseudo code:

class Repository(){
  initialize()// load the first batch
  load(next 10 models)
  save(data)
}

class Store{
   constructor(repository)
   initialize(){
      repository.initialize()
   }
   load(){
      repository.load()
   }
   
   save(){
      repository.save()
   }
}

Now your application data shouldn't be one giant object, rather it should consist of multiple stores, where each store is responsible for a part of the data. For example, you would have one store and repository for handling todos and another pair that handles address book contacts etc.

Addendum:

The reason the repository is injected into the store is so you could easily swap it for some other implementation (the store doesn't care how the data is persisted and retrieved) and also, unit testing is very easy.

You could also have a root store that would hold all other stores, so in essence you have your complete state in one place. So if you call serialize on the root store, it serializes all stores and returns one big object.

Solution 3

AFAIK, there are two options for using sqlite with redux persist.

  1. redux-persist-sqlite-storage: By maintainer's own word

By default redux-persist uses AsyncStorage as storage engine in react-native. This is a drop-in replacemet of AsyncStorage. The library is inspired by react-native-sqlite-storage.

Please, remember, to use this, you need to install an additional package installed react-native-sqlite-storage

  1. redux-persist-sqlite: By maintainer's own word

A redux-persist storage adapter that writes to sqlite. This is adapted from https://github.com/prsn/redux-persist-sqlite-storage, but uses Node.js sqlite3 rather than react-native. Great for Electron apps that are backed by Redux.

UPDATE: react-native-mmkv : This is developed by WeChat. As it says in its about section

An extremely fast key/value storage library for React Native. ~30x faster than AsyncStorage!

Solution 4

I think the best solution would be bloc hydrated or cubit hydrated package from flutter_bloc.

https://pub.dev/packages/hydrated_bloc

In background it uses Hive DB, very performant DB, and only keys are stored in memmory so it should not add huge bloat to the app like SQLite. If you could make all you APP logic in blocks/cubits, then extra DB calls would be irelevant.

Share:
766
ofundefined
Author by

ofundefined

Currently I develop apps and systems for businesses, but actually Physics and Math are my love.

Updated on December 28, 2022

Comments

  • ofundefined
    ofundefined about 1 year

    Mobx and Redux will normally not persist any data. They will maintain a temporary global state while the app is running.

    I know there are redux-persist and mobx-persist packages within both communities. But unfortunately these persisting solutions do not seem good at all. They only stringify or serialize a global state tree and persist it using some sort of key-value storage. Right?

    The problem:

    When such an app is open again, the stringified store will be parsed and structured back to its original data structure (JSON, for instance) and then fully loaded into the RAM memory. Am I right?

    If yes, this is a problem. It is not good to always have a full "database" aka "global state" loaded in-memory. It will probably never be faster to filter data within a long array in my global state... compared to querying a table on SQLite, right?

    I have been looking for some repository-like solution for persisting global state for either redux or mobx. I am yarning for some solution for persisting and querying data on some well-known mobile database like SQLite or others.

    Any answers will be very much appreciated.

  • ofundefined
    ofundefined almost 3 years
    I'm glad you came to my rescue. I'm not really a fan of mobx, you know, but I totally appreciate it.
  • AmerllicA
    AmerllicA almost 3 years
    @HADA, Welcome to SO, and really bravo for your awesome answer. 🌹 I upvoted you.