Migrating to 3.0
3.0 removes APIs that were deprecated or never meant to be public, types persistence end to end, and fixes how development builds are detected. It also adds official Vue, Svelte and Lit bindings, and an Astro integration. Stores themselves are unchanged: a defineStore definition from 2.x works as it is.
All packages now share one version number, so @quantajs/core, @quantajs/react, @quantajs/devtools, @quantajs/vue, @quantajs/svelte, @quantajs/lit and @quantajs/astro are released together as 3.0.0. Update them together: @quantajs/react and @quantajs/devtools now require the other QuantaJS packages from the same major.
npm install @quantajs/core@3 @quantajs/react@3
Coming from 2.0? Apply Migrating to 2.1 first.
At a glance
| 2.x | 3.0 | |
|---|---|---|
useStore('cart'), hasStore('cart') | useCartStore(), or container.get('cart') / container.has('cart') | removed |
store.notifyAll() | Subscribers are called on every change | removed |
nextTick() | await Promise.resolve() | removed |
reactiveEffect | effect | removed |
CommonMigrations, MigrationManager, createMigrationManager | Plain functions in persist.migrations | removed |
createPersistenceManager | The persist store option | removed |
debounce, Logger, createLogger | Your own utilities; logger and LogLevel remain | removed |
pauseTracking, resumeTracking | untrack(fn) | removed |
sanitizePayload, safeJsonParse, safeJsonReviver | Applied automatically to persisted and hydrated data | removed |
RawActions, GetterDefinitions, ActionDefinition, InferActions, StoreInstance, StoreOptions | See Deprecated types | removed types |
Persistence options typed with any | Typed from the store's state | types |
validator saw the transformed output on save | Sees the slice before transform.out, as on load | behaviour |
Stored keys outside include still loaded | Only persisted keys load | behaviour |
| Dev warnings could print in production browser builds | Off in production builds | behaviour |
Removed APIs
Store lookup by name
useStore(name) and hasStore(name) looked stores up by name and returned them untyped. Call the definition, which is typed and resolves against the right container:
- import { useStore } from '@quantajs/core';
- const cart = useStore('cart');
+ import { useCartStore } from './stores/cart';
+ const cart = useCartStore();
For the rare lookup by name, ask the container: container.get('cart') and container.has('cart'). See Containers.
store.notifyAll()
Subscribers are called on every change, so there is nothing to flush. Remove the calls.
nextTick()
Effects run synchronously when state changes, so nextTick only ever waited for a resolved promise. Use that directly where you still need to yield:
- await nextTick();
+ await Promise.resolve();
- nextTick(() => measure());
+ Promise.resolve().then(() => measure());
@quantajs/react no longer re-exports it either.
reactiveEffect
It was the same function as effect. Rename the import.
Migration helpers
CommonMigrations, MigrationManager and createMigrationManager are gone. A migration is a plain function from the stored data to the next version's shape:
persist: {
adapter: new LocalStorageAdapter('app'),
version: 3,
migrations: {
- 2: CommonMigrations.addProperty('flags', {}),
- 3: CommonMigrations.renameProperty('user', 'currentUser'),
+ 2: (data) => ({ ...data, flags: {} }),
+ 3: ({ user, ...rest }) => ({ ...rest, currentUser: user }),
},
},
createPersistenceManager
Stores create their persistence manager from the persist option, and expose it as store.$persist. There was no supported way to use it on its own.
Utilities
debounce, Logger and createLogger were internal utilities. logger and LogLevel remain, for controlling what QuantaJS itself prints; see Logger. Use your own utilities for application code.
Tracking and sanitising helpers
pauseTracking() and resumeTracking() were the internals behind untrack. Use untrack, which also restores tracking if the function throws:
- const previous = pauseTracking();
- const value = state.count;
- resumeTracking(previous);
+ const value = untrack(() => state.count);
sanitizePayload(), safeJsonParse() and safeJsonReviver() are no longer exported. Persisted data and container snapshots are still sanitised automatically, and the default deserialize still drops __proto__, constructor and prototype keys.
Deprecated types
| Removed | Use |
|---|---|
RawActions | ActionsTree |
GetterDefinitions | GettersTree |
ActionDefinition | ActionsTree |
InferActions | BoundActions |
StoreInstance | Store |
StoreOptions | StoreDefinitionOptions |
Persistence
Persistence is typed from the store's state, without any. Most stores need no changes; TypeScript points out the ones that do.
includeandexcludeaccept only state keys.serializereceives the envelope it actually encodes,{ data, version, timestamp, storeName }. It was typed as receiving the state.deserializereturnsunknown, which is checked before use.migrations,transform.inandvalidatorreceiveStoredState, aRecord<string, unknown>: loaded data is unverified until your validator has checked it.transform.outreceives the state slice.- Custom adapters exchange strings:
read()returnsstring | nullandwrite()takes astring.IndexedDBAdapter.read()returnsnullfor a record that is not a string. PersistedData:storeNameis always set, and the unusedchecksumfield is gone.
Two behaviours changed:
- The validator runs on the same shape both ways. When saving, it now checks the slice before
transform.out, the same state-shaped data it checks aftertransform.inwhen loading. If your validator was written against the transformed output, update it. - Only persisted keys load. Removing a key from
include, or adding it toexclude, now also stops its previously stored value from loading into state.
See Persistence for the full order of steps on save and load.
Development builds
QuantaJS now detects development from process.env.NODE_ENV, which your bundler replaces at build time.
- Production browser builds are quiet. Before, the check could not be resolved by bundlers and fell back to development, so apps printed QuantaJS development warnings in production.
- DevTools appear in development. In Vite apps,
mountDevTools()and<QuantaDevTools />now show the panel in development withoutvisible.
Nothing to change in your code.
React
<QuantaProvider>without acontainer, anduseLocalStore, now work under StrictMode. StrictMode's simulated unmount used to dispose the container they went on using.useLocalStorenow re-renders the component when its store changes, likeuseQuanta. If you worked around this with a manual subscription, remove it.shallownow lives in@quantajs/core.@quantajs/reactstill re-exports it, so imports keep working.
New in 3.0
- Vue and Svelte bindings:
@quantajs/vueand@quantajs/svelte, with the sameuseQuanta,useQuantaValue,useQuantaActionsanduseLocalStoreas React. - Lit bindings:
@quantajs/lit, the same four as reactive controllers for web components. - Astro integration:
@quantajs/astrogives each request its own container, carries server state to the islands, and lets React, Vue and Svelte islands share one store. setDefaultContainerResolver, for any server that renders components without passing a container: resolve the default container per request, for example fromAsyncLocalStorage. See Containers.CookieAdapter, to persist small state, such as a theme, in a cookie the server also receives. See Persistence.watchtakesequals, to compare a source's values withshallowor your own function instead ofObject.is. See watch.
Spot something that needs improving?
Edit on GitHubEdit page