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

Hydration deletes properties on sub-properties added by constructor

Open AdamGerthel opened this issue 6 years ago • 8 comments

I have a stumbled on a problem with nested objects where I pass a value to the child, like so:

  constructor(foo) {
    this.someValue = foo;
  }

After hydrating, the value is no longer there. Here's a reproducible example: https://codesandbox.io/s/qvp7x01x59

In the example I'm creating a store along with a child object created by another class. A value is passed to the child when instantiated, and is accessible directly after the store has been created. However, after being hydrated, it's lost.

Update: I'm guessing that this has to do with createSimpleSchema and not createModelSchema being used. I would gladly do a PR but I'm having trouble setting up environment and grasping the code since I'm not used to neither xcode nor typescript. It would be neat if the end result would be a third parameter being passed, something like:

@persist('object', Model, params)
@observable
new Model(params)

AdamGerthel avatar Sep 10 '18 12:09 AdamGerthel

@AdamGerthel I have a same problem after updating mobx version from 4.2.1 to 5.5.2 on react-native 0.57.3 .

JeffGuKang avatar Oct 30 '18 09:10 JeffGuKang

@JeffGuKang actually - I've come to the conclusion that this is due to the fact that in order to hydrate/re-hydrate, mobx-persist needs to be able to properly serialize the data in the store. This is basically impossible to do without knowing the structure/model of the data. So - there are two options, I think: Either make proper use of customArgs (see https://github.com/pinqy520/mobx-persist#createconfig), which is not very well documented and might take a lot of time or drop mobx-persist entirely and switch to Mobx-state-tree instead.

Mobx-persist is simple to use for simple trees, but for complex situations, it's not as easy to use. I think that's basically one of the reasons why Mobx-state-tree was born in the first place.

You could of course also write your own solution, which might be a viable solution depending on how complex your data is.

AdamGerthel avatar Oct 30 '18 09:10 AdamGerthel

@AdamGerthel I am using this module for list type having custom class as yours and it worked well before. I am considering how to resolve this problem and will share my solution after solving it even I am not sure what I choose now.

Anyway, Your comment is very helpful. Thank you for sharing information.

JeffGuKang avatar Oct 30 '18 12:10 JeffGuKang

I solved by myself through autorun. My observable variable is list type that will has a class object having constructor. And it works well.

import {observable, computed, action, autorun, toJS, toJSON} from 'mobx';

class LetterStore {
    @observable letterChannels = []; // LetterChannel 
    @observable isNew = false;
    currentChannelId = null;
    firstLetterCount = 0; // Use for Advertisement

    setCurrentChannelId = (id) => {
        this.currentChannelId = id;
    }

    constructor() {
        let firstAutorun = true;

        /**
         * Autosave observable value
         * TODO: Compare class params with each object in list 
         */
        autorun(async () => {
            console.log('Autorun: ', firstAutorun, this.letterChannels);
            if (firstAutorun) {
                const json = await AsyncStorage.getItem("letterChannels");
                if (json != null) {
                    this.letterChannels = JSON.parse(json);
                }
                firstAutorun = false;
            } else {
                const json = toJSON(this.letterChannels);
                await AsyncStorage.setItem("letterChannels", json)
            }
        });
    }

....

JeffGuKang avatar Nov 01 '18 06:11 JeffGuKang

Actually, I think your use-case should work with Mobx-persist out of the box (but it depends on what your list contains). My issue was with sub-classes.

Anyway - another option would be to pass your existing data to the class constructor. You would have to fetch the data from storage before initiating the store, but I think it's a little nicer to keep that logic out of the class.

AdamGerthel avatar Nov 01 '18 07:11 AdamGerthel

@AdamGerthel My observable list contains classes having sub classes. And I think your recommendation is better to use in production. Thank you :)

JeffGuKang avatar Nov 01 '18 07:11 JeffGuKang

@JeffGuKang Glad I could help :)

If your problem grows (i.e. if your app becomes more complex) then I would recommend to consider switching to MobX State Tree since it can handle hydration out of the box.

AdamGerthel avatar Nov 01 '18 07:11 AdamGerthel

For other's who stumble on mobx-persist problems and land here, I had trouble persisting JSON objects with lots of sub-properties. I hacked around this by persisting only strings from JSON.stringify(bigJSON), and then creating computed @get properties that return JSON.parse(bigJSONString) whenever I need it in the app. So long story short, I did the serializing myself into a string because I wasn't sure how to make the built in serializing work.

tomchify avatar Aug 13 '19 17:08 tomchify