[Resolved] Aurelia-store: advice on proper use of localStorageMiddleware

I’ve integrated aurelia-store in my app and for the most part it is working well, however my local state is getting overwritten as referenced in “Managing App State”

Keep in mind that the store starts with an initialState . If the localStorage middleware is registered at the app’s start, most likely the next refresh will immediately overwrite your localStorage and negate the effect of restoring data from previous runs. In order to avoid that, make sure to register the middleware just after the initial state has loaded.

It’s not clear to me how to determine when the “initial state” has loaded. Can someone point me in the right direction?

Thanks!

1 Like

FWIW, here’s my naive attempt to detect when the initialState has been loaded in my app.ts bind() method:

async bind(bindingContext: any, overrideContext: any) {
		applicationLogger.info("app.bind() called.");
		this.stateSubscription = this.store.state.subscribe(async (state) => {
			if (!this.initialStateLoaded) {
				this.initialStateLoaded = true;
				// LocalStorageMiddleware should be the last one in the chain.
				await this.store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, { key: greenhouseStorageKey });
				await this.store.dispatch(rehydrateFromLocalStorage, greenhouseStorageKey);
			} else {
				this.stateSubscription.unsubscribe();
			}
			
		});
	}

Note: I am registering the “Rehydrate” action in my app.ts constructor:

	this.store.registerAction('Rehydrate', rehydrateFromLocalStorage);

My local storage is still getting overridden.

In my project, I have opted to setup and call everything in the App's constructor.

export class App {
    constructor(private store: Store<IAppState>) {
        this.store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, settings);
        this.store.registerAction("REHYDRATE_STATE", rehydrateFromLocalStorage);

        // other app wide store actions here

        this.store.dispatch("REHYDRATE_STATE");
    }
}

As a result, I haven’t had any issues with localStorage getting overridden. Hope this helps.

2 Likes

@rowellx68 Thanks for your reply. I originally had my code organized as you have described. I have tracked my issue down to another action which was overwriting the the store due to a bug that I managed to introduce while programming while I was sick and not thinking clearly. :frowning:

1 Like

Similar to others, I have a loadState function called in the app constructor as follows:

    loadState()
    {
        logger.debug("App: loadState");

        let state = localStorage.getItem("state");

        if (state !== null)
        {
            let newState : State = JSON.parse(state);
            this.store.dispatch(STATE_ACTIONS.loadStateChangeAction, newState);
        }
        else
        {
            const newState = initialState;
            this.store.dispatch(STATE_ACTIONS.loadStateChangeAction, newState);            
        }
    }

Now, you have to make sure that you are actually saving the state into localStorage or its just going to keep using the default state every time. I initially was trying to connect into the beforeunload DOM event, and found out that was actually never firing when the app was refreshed with F5, which I needed for my app.

I ended up just saving into localStorage each time the state changed using the following when you use the @connectTo decorator.

stateChanged()
{
        logger.debug("App: stateChanged");

        let saveState = JSON.stringify(this.state);
        localStorage.setItem("state", saveState);
}
1 Like

@airboss001 Thanks for for sharing your solution. You’ve given me something to think about.

1 Like

Glad to see you guys got this handled.

@airboss001 Any reason why you rolled your own implementation over using the provided middleware? The middleware saves you from having to manually save the state to local storage when it changes.

1 Like

@rowellx68 I would probably say ignorance mainly.

Missed that on the guide page the first 3 or 4 times through the document. Sometimes there is so much information packed into a single page (sometimes just a sentence!) its easy to miss how to make things easy (ssshh, don’t let that get out!) if that isn’t what you are looking at finding a solution for.

BTW, thanks for taking the time to point that out and educate me.

1 Like

I am using the middleware to handle page refreshes in my app. The problem I run into is that whenever I add a new property in the state, because I am using the middleware it will not pickup that change and re hydrating will put what ever is in my localstorage as my current app state. I have to clear my local storage before it all works fine.
Please can anyone hightlight on how to handle this.

I’m not sure I follow.

How are you adding properties to the state? (Via actions or what else?)
What exactly is not picking up the new state?

Also, are you waiting for your actions to complete?
Store#dispatch() returns a promise, so maybe you are navigating away too soon and interrupt the action?

@mananshah1403 would it be possible to post a snippet of how you are registering the middleware?

For my setup, I have registered the middleware to kick in after the state has changed. I do this with the middleware placement option.

this.store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, settings);