Closed j-maas closed 6 years ago
It is decision to make here. I am not sure how this will go down.
My 10000 foot overview opinion is this -
Do not store any non-primitive data inside stores that serialize and deserialize in unexpected ways
I would recommend not storing Date objects into vuex. Or if you do, handle serialization and deserialization using your own custom getters and setters.
Keeping this open for discussion. If we should deserialize items based on their type or expose an interface to define custom deserializers or not.
I guess we cannot just read a string, and infer if it might be a date or not (without humongous pattern matching penalties)
I'm fine with manually overwriting the deserialization. How do I do that?
With localForage it is possible to store objects quite easily. Unfortunately, it will not restore them to the correct type when loading, since it does not support custom types. The prototype
will not be set correctly.
To fix this I used cerialize which allows quite easily to make custom classes properly (de-)serializable. It is then only necessary to make vuex-persist execute the deserialization using the restoreState
config option.
...
import { autoserialize, autoserializeAs, Deserialize } from 'cerialize';
...
class Apple {
@autoserialize color = 'red';
}
class Bag {
@autoserializeAs(Apple) contents = [ new Apple(), new Apple(), new Apple() ];
}
class State {
@autoserialize primitive: string = 'just a simple string';
@autoserializeAs(Bag) bag: Bag = new Bag();
}
const mutationTree: MutationTree<State> = {
'add-apple': (state: State, payload: { apple: Apple }) => { state.bag.contents.push(apple) },
}
const vuexLocal = new VuexPersistence({
strictMode: true,
storage: localForage,
async restoreState(key: string): Promise<State> {
const state = await localForage.getItem(key)
const deserialized = Deserialize(state, State)
return deserialized
},
asyncStorage: true,
})
mutationTree['RESTORE_MUTATION'] = vuexLocal.RESTORE_MUTATION // Add missing mutation for strictMode
const store = new Vuex.Store<State>({
strict: true,
state: new State(),
mutations: mutationTree,
actions: {},
plugins: [
vuexLocal.plugin,
],
})
new Vue({
...,
store,
...,
})
When a
Date
object is in the state, it is saved to local storage serialized as a string. Upon reload of the page, the date remains a string.Excerpt from error in browser console after using the button to add a date and refreshing the page:
TypeError: date.toLocaleDateString is not a function
Reproduction
You can checkout my reproduction repo. It is a minimal Vue app generated with the default vue-cli (v3) scaffold.
To reproduce the bug, follow these steps:
yarn serve
.Notes
If you do not hit the "+" button, it works, even though there is a date already initialized from the default state. So if you refresh the page with an empty local storage, everything works as expected. I assume that vuex-persist does not save the state to local storage yet, since nothing has triggered a change.
In the production app I found this in, I use TypeScript. The bug is the same there.