mst-persist icon indicating copy to clipboard operation
mst-persist copied to clipboard

Persist changed value from `true` to `false`

Open ghost opened this issue 4 years ago • 3 comments

Hi.

Environment info

React native info output:

 System:
    OS: Linux 5.3 Ubuntu 18.04.4 LTS (Bionic Beaver)
    CPU: (8) x64 Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
    Memory: 12.78 GB / 31.23 GB
    Shell: 4.4.20 - /bin/bash
  Binaries:
    Node: 12.16.1 - /usr/bin/node
    Yarn: 1.21.1 - /usr/bin/yarn
    npm: 6.13.4 - /usr/bin/npm
  npmPackages:
    react: 16.9.0 => 16.9.0 
    react-native: 0.61.5 => 0.61.5

The problem is, when i persist RootStore with nested AuthStore, all my boolean value form AuthStore become from true to false.

When i just console.log its value, i have this behaviour

const { isLoading } = authStore;

console.log('isLoading => ', isLoading);

image

It is RootStore with nested AuthStore

import { types } from 'mobx-state-tree';
import { connectReduxDevtools } from 'mst-middlewares';
import AuthStore from './auth.store';
import { persist } from 'mst-persist';
import AsyncStorage from '@react-native-community/async-storage';

let store: any = null;

export default () => {
  if (store) return store;

  const RootStore = types
    .model('RootStore', {
      identifier: types.optional(types.identifier, 'RootStore'),
      auth: AuthStore,
    });

  store = RootStore.create({
    auth: {
      isLoading: true,
    },
  });

  /**
   * Not working in debug mode
   */
  persist('rootStore', store, {
    storage: AsyncStorage,
  }).then(() => {});

  // @ts-ignore
  if (__DEV__ && Boolean(window.navigator.userAgent)) {
    connectReduxDevtools(require('remotedev'), store);
  }

  return store;
};

There is AuthStore

import { types } from 'mobx-state-tree';
const AuthStore = types
  .model({
    isLoading: types.optional(types.boolean, true),
  });

export default AuthStore;

ghost avatar Feb 26 '20 19:02 ghost

Can i persist only nested store? How can i add isLoading to whiteList if it is nested store?

ghost avatar Feb 26 '20 19:02 ghost

Thanks for narrowing this down to a more minimal example @YanisKondakov , was hard to read through before.

You still didn't quite provide an example of where this code is run:

const { isLoading } = authStore;

console.log('isLoading => ', isLoading);

For it to log twice, it must've been run twice, and that's not in the code you've listed. authStore also isn't defined anywhere.

Since you have isLoading defaulted to true, this shouldn't be affected by #5 , it should initialize an empty storage to true as well. It sounds like your problem is that isLoading is changed to false in your app somewhere, and then false gets persisted. So when your app hydrates data from storage, it will hydrate to false.

Can i persist only nested store?

Any persisted store will persist all its nested stores as well. But I'm struggling to fully understand what you meant here as there's some grammatical errors.

How can i add isLoading to whiteList if it is nested store?

Did you mean to blacklist? In either case, you've hit the mark here, there's no way to specifically whitelist or blacklist a nested property right now. You can blacklist auth, but not auth.isLoading.

This is a problem I'm looking to fix soon once I've got Transforms #16 merged in, likely using a similar dotted syntax as above. Deep blacklists are relatively easy to implement, but deep whitelists are more complex (especially if any conflict handling is added). This has popped up in my own usage as well, such as https://github.com/agilgur5/react-native-manga-reader-app/issues/27, so it's a known issue, but I never filed a formal issue for it. Will do that now. EDIT: see #27

There are some workarounds for this, but they're all a bit hacky without Transforms 😕 Like one is to manually set your storage to the default of true. Sorry about that.


Side note:

isLoading: types.optional(types.boolean, true)

can just be simplified to:

isLoading: true

Per https://mobx-state-tree.js.org/concepts/trees#creating-models , they're equivalent.

agilgur5 avatar Feb 27 '20 03:02 agilgur5

Thank you @agilgur5 for the detailed answer. And sorry for my english.

The example of where this code is run

const { isLoading } = authStore;

console.log('isLoading => ', isLoading);

is

import React from 'react';
import createStore from './stores';
import { inject, observer, Provider } from 'mobx-react';

//This is a RootStore
const store = createStore();

const App = inject(({ authStore }) => ({ authStore }))(
  observer(({ authStore }: any) => {
   const { isLoading } = authStore;
  
   console.log('isLoading => ', isLoading);

    if (isLoading) {
       return <SplashScreen />;
    }

    return (...);
  }),
);

const AppProvider = () => (
  <Provider rootStore={store} authStore={store.auth} profileStore={store.profile}>
    <App />
  </Provider>
);

export default AppProvider;

It sounds like your problem is that isLoading is changed to false in your app somewhere, and then false gets persisted. So when your app hydrates data from storage, it will hydrate to false.

As you can see, isLoading is not changed to false anywhere. And if i remove data hydrating, isLoading log only once and it's value is true.

ghost avatar Feb 27 '20 11:02 ghost