[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[ ] Performance issue
[X] Feature request
[ ] Documentation issue or request
[ ] Support request
[ ] Other... Please describe:
Persiststate plugin works great, but does not handle changes to data models well. Could be very dangerous to reload a model that is no longer compatible with the app.
Needs some kind of version number that can be incremented when there are breaking changes to the datamodel.
The simplest way would be to have a single version number defined in the options for the persistState plugin:
persistState({
version: 17,
include: [ 'checkoutState', 'cartItem' ],
key: 'ShoppingCart'
});
Then if I change my data model, such as renaming a field I'd increment version to version: 18 and a reload of the persisted data would fail silently if the version didn't match (so same as if I had nothing persisted at all).
Beyond that - if you wanted to get more advanced, each store could have a version number (the data model version) - which you'd be able to increment. Then only that store would fail loading. So if you don't have to purge the entire model.
Another idea could be an upgrade callback where you were presented with (model: any, oldVersion: number, newVersion: number) { ... } and you could upgrade the data. Personally that wouldn't interest me - my users data isn't that important!
Personally for me I'm just storing a shopping cart! - so I'm fine with a single global version number for safety.
If in doubt - increment the version.
I realize of course that persistence can be a very complicated issue and requirements vary wildly, but I think it's important to remind people about the importance of versioning when explaining the persistence plugin - so along with that some simple options would be great.
Data integrity
Safety when making changes that may or may not be breaking changes.
N/A
Others:
Thanks for the continuing new features :-)
After a discussion with the team, we decide not to add it into the persist-state at this moment. We'll consider it again if more developers will request it.
In the meantime, our suggestion is to use the akitaPreUpdate hook:
@StoreConfig({ name: 'cart' })
export class CartStore extends Store<CartState> {
constructor() {
super();
}
akitaPreUpdate(prevState: CartState, nextState: CartState) {
// check the model and change it if you need.
}
}
https://netbasal.gitbook.io/akita/entity-store/store-middleware
Or handle it by using plain JS. Thanks for the suggestion, keep up with the right ideas.
Here's a q'n'd snippet that should take care of most (edit: simple, within the same store) use-cases.
The persisted state is wrapped in a { $$version, state } object, non-versioned state is automatically migrated to { $$version: - 1, state }
migrationMap holds a per-storename map of migration functions. These are run in order until the target version (i.e. index and return value of the last function) is reached, already applied mgrations are skipped.
// license: MIT
import { HashMap, persistState } from '@datorama/akita';
type StoreMigrationFn<A = any, B extends {} = {}> = (state: A) => B;
interface VersionedState<T = any> {
$$version: number;
state: T;
}
const migrationMap: HashMap<StoreMigrationFn[]> = {
auth: [
() => ({}), // first migration, just clears out any non-versioned state
state => ({ // second migration, renames state.jwt to state.token
token: state.jwt,
})
]
};
export function initPersistState() {
persistState({
include: ['auth'],
preStorageUpdate(storeName: string, state: any): VersionedState {
const migrations = migrationMap[storeName] || [];
return {
$$version: migrations.length - 1,
state
};
},
preStoreUpdate(storeName: string, rawState: any): any {
const migrations = migrationMap[storeName] || [];
const storageVersion = assertVersionedState(rawState);
const targetVersion = migrations.reduce((currentVersion, migrate, i) => {
if (currentVersion.$$version >= i) {
return currentVersion;
}
return {
$$version: i,
state: migrate(currentVersion.state)
};
}, storageVersion);
return targetVersion.state;
}
});
}
function assertVersionedState(state: any): VersionedState {
if (typeof state.$$version === 'number') {
return state;
} else {
return { $$version: -1, state: state || {} };
}
}
Most helpful comment
Here's a q'n'd snippet that should take care of most (edit: simple, within the same store) use-cases.
The persisted state is wrapped in a
{ $$version, state }object, non-versioned state is automatically migrated to{ $$version: - 1, state }migrationMapholds a per-storename map of migration functions. These are run in order until the target version (i.e. index and return value of the last function) is reached, already applied mgrations are skipped.