# Aurelia Store - Initial state and local storage

**URL:** https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184
**Category:** Framework Knowledge
**Created:** [January 23, 2020, 11:05am UTC](https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184 "2020-01-23T11:05:32Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![TomDoig](https://avatars.discourse-cdn.com/v4/letter/t/ebca7d/32.png) [@TomDoig](https://discourse.aurelia.io/u/TomDoig)
#### Post date: [January 23, 2020, 11:05am UTC](https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184/1 "2020-01-23T11:05:32Z")

</div>

Hi all,

I’ve been using Aurelia Store to persist my local state into local storage.

I’ve noticed that if any changes are made to my `initialState` declaration (e.g new properties, or changes to default values), they are discarded when the `rehydrateFromLocalStorage` action is dispatched on refresh.

My current approach is :

```auto
export class App() {
    constructor(private store: Store<State>) {
        // Register the middleware
         store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, {key: 'storage-key' });
        
        // Register the rehydration action
        store.registerAction('Rehydrate', rehydrateFromLocalStorage);

        // ... Other state actions declared here

        dispatchify('Rehydrate')('storage-key');
    }
}

```

If I were to subscribe to the state, add a new property to the initial state, and refresh - I can see that the new initial state is loaded correctly _until_ the rehydrate action is dispatched.

At this point the state subscriber returns exactly what was stored in local storage before the changes, excluding any new/updated properties or default values.

Do I need to change the way I’m configuring the plugin, or is there a standard practice for managing changes like this?

Thanks in advance for your time!

Cheers,  
Tom

---

<div class="post-metadata">

### Author: ![zewa666](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/zewa666/32/19_2.png) [@zewa666](https://discourse.aurelia.io/u/zewa666)
#### Post date: [January 23, 2020, 11:20am UTC](https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184/2 "2020-01-23T11:20:29Z")

</div>

That would require the store to know how to diff/merge the two states which is a very generic task. In this case I’d recommend to build your own [rehydrate action](https://github.com/aurelia/store/blob/master/src/middleware.ts#L33-L48) and do the sanitization/merging right in there

so something along the lines of

```auto
import {rehydrateFromLocalStorage} from "aurelia-store";

export function customRehydrate(state: State, key?: string) {
   const newState = rehydrateFromLocalStorage(state, key);

   // modify newState according to your needs but keep the shallow cloning in mind

  return newState;
}

```

---

<div class="post-metadata">

### Author: ![jeremyholt](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/jeremyholt/32/1098_2.png) [@jeremyholt](https://discourse.aurelia.io/u/jeremyholt)
#### Post date: [January 23, 2020, 8:54pm UTC](https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184/3 "2020-01-23T20:54:17Z")

</div>

I had the same problem and came up with

```auto
import { autoinject } from "aurelia-framework";
import { Store } from "aurelia-store";
import _ from "lodash";
import { initialState } from "./initial-state";
import { IState } from "./state";

@autoinject
export class StateInitializationService {
  constructor(
    private store: Store<IState>
  ) {
    store.registerAction("initializeState", initializeStateAction);
  }

  public init(state: IState) {
    Object.keys(initialState).forEach(key => {
      if (state && state[key] === undefined) {
        state[key] = initialState[key];
      }
      if (initialState[key]?.hasOwnProperty("current") && !state[key].current) {
        state[key].current = initialState[key].current;
      }
    });

    state.serverMessages.errorMessage = undefined;
    state.serverMessages.message = undefined;

    this.store.dispatch(initializeStateAction, state);
  }
}

export function initializeStateAction(state: IState, response: IState) {
  let newState = _.cloneDeep(state);
  newState = response;
  return newState;
}

```

`app.ts`

```auto
constructor(
    store: Store<IState>
  ) {
       store.registerAction("Rehydrate", rehydrateFromLocalStorage);

       if (localStorage[LOCAL_STORAGE.state]) {
      store.dispatch(rehydrateFromLocalStorage, LOCAL_STORAGE.state);
    }

    store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, { key: LOCAL_STORAGE.state });
  }

protected bind() {
    this.stateInitializationService.init(this.state);
  }

```

---

<div class="post-metadata">

### Author: ![TomDoig](https://avatars.discourse-cdn.com/v4/letter/t/ebca7d/32.png) [@TomDoig](https://discourse.aurelia.io/u/TomDoig)
#### Post date: [January 24, 2020, 2:41pm UTC](https://discourse.aurelia.io/t/aurelia-store-initial-state-and-local-storage/3184/4 "2020-01-24T14:41:33Z")

</div>

Thanks for the advice, [zewa666](https://discourse.aurelia.io/u/zewa666) and [jeremyholt](https://discourse.aurelia.io/u/jeremyholt).

I’ve ended up using a combination of both of these suggestions :

```auto
export function customRehydrateAction(state: State, key?: string) {
    return syncState(
        {... state},
        rehydrateFromLocalStorage(state, key)
    ) as State;
}

// Recursively syncs two given state objects
function syncState(initialState: {}, fromStorage: {}) {
    Object.keys(initialState).forEach(key => {
        // If we don't have a value in our local storage, leave the initial value as is.
        if (!(key in fromStorage)) {
            return;
        }

        // If both keys are Objects, excluding arrays, recursively sync the entries of each.
        if (
            isObject(initialState[key]) &&
            isObject(fromStorage[key]) &&
            !Array.isArray(initialState[key])
        ) {
            initialState[key] = syncState(initialState[key], fromStorage[key]);
            return; 
        }

        // If we get here, just assign our stored value to our state.
        initialState[key] = fromStorage[key];
    });

    return initialState;
}

```

This appears to be working nicely, and has the added benefits of clearing out keys and values which are no longer present in the initialState.

Thanks again for your help!
