Skip to content

Instantly share code, notes, and snippets.

@lvnam96
Last active May 14, 2023 01:36
Show Gist options
  • Save lvnam96/a7202f7ae6b3116a52bd881783ccee1b to your computer and use it in GitHub Desktop.
Save lvnam96/a7202f7ae6b3116a52bd881783ccee1b to your computer and use it in GitHub Desktop.
svelte-zustand (adapter)
import { toSvelteStore, useStore } from './svelte-zustand-adapter';
import { createStore } from 'zustand';
import { combine } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
const getDefaultInitialState = (): State => ({
data: null,
});
const initializeStore = (preloadedState: Partial<State> = {}) =>
createStore(
immer(
combine(
{
...getDefaultInitialState(),
...preloadedState,
} as State,
(set) =>
({
setData: (payload) => {
set((state) => {
state.data = payload;
});
},
removeData: () => {
set((state) => {
state.data = getDefaultInitialState().data;
});
},
} as Actions),
),
),
);
export const store = toSvelteStore(initializeStore());
// SELECTORS
const getAbcData = (state: ModuleState) => state.data;
// "REACT HOOK"-ALIKE HELPER FUNCTONS
export const useAbcStore = <T>(
selector?: Parameters<typeof useStore<typeof store, T>>[1],
) => useStore(accountStore, selector);
export const useAbcData = () => useAbcStore(getAbcData);
export const useSetData = () => useAbcStore((state) => state.setData);
// TYPES
interface State {
data: {
id: number;
//...
} | null;
}
interface Actions {
setData: (payload: State['data']) => void;
removeData: () => void;
}
type ModuleState = State & Actions;
// type ModuleStore = ReturnType<typeof createStore<ModuleState, [['zustand/immer', never]]>>; // add `['zustand/subscribeWithSelector', never]` if you use `zustand/middleware/subscribeWithSelector`
import { readable, type Readable } from 'svelte/store';
import type { useStore as _useStore, StoreApi } from 'zustand';
// inspired by https://jfranciscosousa.com/blog/using-zustand-with-svelte/
export function toSvelteStore<Store extends StoreApi<StateType>, StateType>(zustandStore: Store) {
const svelteStore = readable(zustandStore.getState(), (set) => {
zustandStore.subscribe((value) => set(value));
});
return {
...zustandStore,
...svelteStore,
subscribe: svelteStore.subscribe,
zustandSubscribe: zustandStore.subscribe,
} as Readable<ReturnType<Store['getState']>> &
Store & {
zustandSubscribe: Store['subscribe'];
};
}
// inspired by https://github.com/JoaoPauloLousada/ngx-zustand
export function useStore<S extends StoreApi<StateType>, T, StateType = unknown>(
store: S,
selector?: Parameters<typeof _useStore<S, T>>[1],
// equalityFn?: Parameters<typeof _useStore<S, T>>[2], // this arg is only available with original React hook `useStore` exported from `zustand`
): T;
export function useStore<S extends StoreApi<unknown>>(
store: Parameters<typeof _useStore<S>>[0],
): ReturnType<typeof _useStore<S>>;
export function useStore<S extends StoreApi<StateType>, T, StateType>(
store: Parameters<typeof _useStore<S, T>>[0],
selector?: Parameters<typeof _useStore<S, T>>[1],
) {
const state = store.getState();
return selector ? selector(state as Parameters<Parameters<typeof _useStore<S, T>>[1]>[0]) : state;
}
<script lang="ts">
import { store, useAbcData, useSetData } from './store';
// you can import store & access its prop directly:
$: console.log('new changes will be logged here', $store.data);
// or grab its data via helper function:
const data = useAbcData(); // note that `data` is not subscribed to changes (like it should be if returned from a "react hook"). If you need to, use `$store.data` instead
const setData = useSetData(); // same goes here
</script>
<button on:click={() => {
store.getState().setData({
id: Math.random(),
});
// OR:
// setData({
// id: Math.ramdom(),
// });
}}>
Set random data
</button>
{$store.data}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment