This package basically acts as a wrapper for writable stores.
+page
is also supported.this
keyword to manage your state.$init
eg. $init: 0
, then get(store)
return 0
.this
keyword. read reference type, for more details...npm install svelte-exstore
yarn add svelte-exstore
pnpm add svelte-exstore
src/lib/store/count.ts
import { ex } from "svelte-exstore";
interface Count {
$init: number;
increase(): void;
decrease(): void;
increaseBy(by: number): void;
reset(): void;
}
export const count = ex<Count>({
$name: 'count', // store name displayed in devtools, must be unique.
$init: 0,
increase() {
this.$init += 1; // retrieve your current state with `this` keyword.
},
increaseBy(by) {
this.$init += by;
},
decrease() {
this.$init -= 1;
},
reset() {
this.$init = 0;
}
});
src/routes/+page.svelte
<script lang="ts">
import { count } from '$lib/store/count';
</script>
<h1>{$count}</h1>
<!-- $count is an alias for count.$init -->
<button on:click={() => count.increase()}>+</button>
<button on:click={() => count.increaseBy(5)}>increase by 5</button>
<button on:click={() => count.reset()}>reset</button>
$init
-- get(store)
will return $init
count.ts
interface Count {
$init: number;
increase: () => void;
}
const count = ex<Count>({
$name: 'count-test-store',
$init: 0,
increase() {
this.$init += 1;
}
});
Count.svelte
<h1>{$count}</h1>
<!-- $count is an alias for count.$init -->
primitive type
, the action can also return the value like this.count.ts
interface Count {
$init: number;
increase: () => void;
}
const count = ex<Count>({
$name: 'count-test-store',
$init: 0,
increase() {
return this.$init + 1; // support only primitive type.
}
});
this
keyword.profile.ts
interface Profile {
name: string;
age: number;
description?: string;
increaseAgeBy: (value:number) => void;
}
const profile = ex<Profile>({
$name: 'profile-test-store',
name: '',
age: 20,
increaseAgeBy(value){
this.age += value;
}
})
Profile.svelte
<h1>{$profile.name}</h1>
<h2>{$profile.age}</h2>
<h2>{$profile.description ?? ''}</h2>
store.subscribe()
, store.set()
and store.update()
are also available.profile.update((state) => {
state = { name: 'Jack', age: 30 };
return state;
});
profile.set({});
Profile.svelte
<button on:click={() => { profile.set({}); }}> Reset Name </button>
store.subscribe()
now provide readonly state by default to prevent unpredictable state change.profile.subscribe((value) => {
console.log('stage 9: readonly reference', value);
// if uncomment this, it should throw an error. because the state is readonly.
// value.name = 'Jane';
});
setupTests.ts
vi.mock('$app/stores', async () => {
const { readable, writable } = await import('svelte/store');
/**
* @type {import('$app/stores').getStores}
*/
const getStores = () => ({
navigating: readable(null),
page: readable({ url: new URL('http://localhost'), params: {} }),
session: writable(null),
updated: readable(false)
});
/** @type {typeof import('$app/stores').page} */
const page = {
subscribe(fn: () => void) {
return getStores().page.subscribe(fn);
}
};
/** @type {typeof import('$app/stores').navigating} */
const navigating = {
subscribe(fn: () => void) {
return getStores().navigating.subscribe(fn);
}
};
/** @type {typeof import('$app/stores').session} */
const session = {
subscribe(fn: () => void) {
return getStores().session.subscribe(fn);
}
};
/** @type {typeof import('$app/stores').updated} */
const updated = {
subscribe(fn: () => void) {
return getStores().updated.subscribe(fn);
}
};
return {
getStores,
navigating,
page,
session,
updated
};
});
vi.mock('$app/environment', async () => {
/** @type {typeof import('$app/environment').browser} */
const browser = true;
/** @type {typeof import('$app/environment').dev} */
const dev = true;
/** @type {typeof import('$app/environment').prerendering} */
const prerendering = false;
return {
browser,
dev,
prerendering
};
});