How to save Mobx state in sessionStorage How to save Mobx state in sessionStorage reactjs reactjs

How to save Mobx state in sessionStorage


The easiest way to approach this would be to have a mobx "autorun" triggered whenever any observable property changes. To do that, you could follow my answer to this question.

I'll put some sample code here that should help you get started:

function autoSave(store, save) {  let firstRun = true;  mobx.autorun(() => {    // This code will run every time any observable property    // on the store is updated.    const json = JSON.stringify(mobx.toJS(store));    if (!firstRun) {      save(json);    }    firstRun = false;  });}class MyStore {  @mobx.observable prop1 = 999;  @mobx.observable prop2 = [100, 200];  constructor() {    this.load();    autoSave(this, this.save.bind(this));  }  load() {    if (/* there is data in sessionStorage */) {      const data = /* somehow get the data from sessionStorage or anywhere else */;      mobx.extendObservable(this, data);    }  }  save(json) {    // Now you can do whatever you want with `json`.    // e.g. save it to session storage.    alert(json);  }}


Posting the example from here: https://mobx.js.org/best/store.html

This shows a cleaner method of detecting value changes, though not necessarily local storage.

import {observable, autorun} from 'mobx';import uuid from 'node-uuid';export class TodoStore {    authorStore;    transportLayer;    @observable todos = [];    @observable isLoading = true;    constructor(transportLayer, authorStore) {        this.authorStore = authorStore; // Store that can resolve authors for us        this.transportLayer = transportLayer; // Thing that can make server requests for us        this.transportLayer.onReceiveTodoUpdate(updatedTodo => this.updateTodoFromServer(updatedTodo));        this.loadTodos();    }    /**     * Fetches all todo's from the server     */    loadTodos() {        this.isLoading = true;        this.transportLayer.fetchTodos().then(fetchedTodos => {            fetchedTodos.forEach(json => this.updateTodoFromServer(json));            this.isLoading = false;        });    }    /**     * Update a todo with information from the server. Guarantees a todo     * only exists once. Might either construct a new todo, update an existing one,     * or remove an todo if it has been deleted on the server.     */    updateTodoFromServer(json) {        var todo = this.todos.find(todo => todo.id === json.id);        if (!todo) {            todo = new Todo(this, json.id);            this.todos.push(todo);        }        if (json.isDeleted) {            this.removeTodo(todo);        } else {            todo.updateFromJson(json);        }    }    /**     * Creates a fresh todo on the client and server     */    createTodo() {        var todo = new Todo(this);        this.todos.push(todo);        return todo;    }    /**     * A todo was somehow deleted, clean it from the client memory     */    removeTodo(todo) {        this.todos.splice(this.todos.indexOf(todo), 1);        todo.dispose();    }}export class Todo {    /**     * unique id of this todo, immutable.     */    id = null;    @observable completed = false;    @observable task = "";    /**     * reference to an Author object (from the authorStore)     */    @observable author = null;    store = null;    /**     * Indicates whether changes in this object     * should be submitted to the server     */    autoSave = true;    /**     * Disposer for the side effect that automatically     * stores this Todo, see @dispose.     */    saveHandler = null;    constructor(store, id=uuid.v4()) {        this.store = store;        this.id = id;        this.saveHandler = reaction(            // observe everything that is used in the JSON:            () => this.asJson,            // if autoSave is on, send json to server            (json) => {                if (this.autoSave) {                    this.store.transportLayer.saveTodo(json);                }            }        );    }    /**     * Remove this todo from the client and server     */    delete() {        this.store.transportLayer.deleteTodo(this.id);        this.store.removeTodo(this);    }    @computed get asJson() {        return {            id: this.id,            completed: this.completed,            task: this.task,            authorId: this.author ? this.author.id : null        };    }    /**     * Update this todo with information from the server     */    updateFromJson(json) {        // make sure our changes aren't send back to the server        this.autoSave = false;        this.completed = json.completed;        this.task = json.task;        this.author = this.store.authorStore.resolveAuthor(json.authorId);        this.autoSave = true;    }    dispose() {        // clean up the observer        this.saveHandler();    }}


Turns out you can do this in just a few lines of code:

const store = observable({    players: [        "Player 1",        "Player 2",    ],    // ...})reaction(() => JSON.stringify(store), json => {    localStorage.setItem('store',json);}, {    delay: 500,});let json = localStorage.getItem('store');if(json) {    Object.assign(store, JSON.parse(json));}

Boom. No state lost when I refresh the page. Saves every 500ms if there was a change.