Search code examples
javascriptreactjsmobxmobx-reactmobx-persist

Use MobX with React Context while using Mobx Persist Store?


Originally, I was using new CounterStore inside React.createContext()

context.ts

import React from 'react'
import { stores, PersistState, CounterStore } from '@/store/index'
import type { ICounterStore } from '@/types/index'

export const FrameItContext = React.createContext<ICounterStore>(new CounterStore())
export const useCounterStore = () => React.useContext(FrameItContext)

Then I started using Mobx Persist Store in my app.

persist.ts

import { persistence, StorageAdapter } from 'mobx-persist-store'
import { CounterStore } from '@/store/index'

const read = (name: string): Promise<string> =>
    new Promise((resolve) => {
        const data = localStorage.getItem(name) || '{}'
        console.log('got data: ', data)
        resolve(data)
    })

const write = (name: string, content: string): Promise<Error | undefined> =>
    new Promise((resolve) => {
        localStorage.setItem(name, content)
        console.log('write data: ', name, content)
        resolve(undefined)
    })

export const PersistState = persistence({
    name: 'CounterStore',
    properties: ['counter'],
    adapter: new StorageAdapter({ read, write }),
    reactionOptions: {
        // optional
        delay: 2000,
    },
})(new CounterStore())

And I changed my code to use PersistState instead of new CounterStore()

context.ts

import React from 'react'
import { stores, PersistState, CounterStore } from '@/store/index'
import type { ICounterStore } from '@/types/index'

export const FrameItContext = React.createContext<ICounterStore>(PersistState)
export const useCounterStore = () => React.useContext(FrameItContext)

It only logs got data: {} to the console. The write function never gets called.

Is there anything I am doing wrong?

Coincidentally, a simple Counter example on Codesandbox works perfectly fine → https://codesandbox.io/s/mobx-persist-store-4l1dm


Solution

  • The example above works on a simple Chrome extension or a web app but just doesn't seem to work with my specific application so I wrote a manual implementation of saving to LocalStorage.

    Use toJSON() on the store to keep track of which properties should be saved:

    toJSON() {
        const { color, counter } = this
        return {
            color,
            counter,
        }
    }
    

    And add the localStorage logic just below the constructor(). First, check if the localStorage contains latest value & return it, if it does.

    If there is nothing saved, then save it inside localStorage.

    constructor() {
        ...
        const name = "CounterStore"
        const storedJson = localStorage.getItem(name)
        if (storedJson) Object.assign(this, JSON.parse(storedJson))
        autorun(() => {
            localStorage.setItem(name, JSON.stringify(this))
        })
    }
    

    Codesandbox → https://codesandbox.io/s/mobx-persist-store-manual-implementation-vm38r?file=/src/store.ts