let rootStore: RootState
-export function devtoolPlugin<S extends StateTree>(store: Store<S>) {
+export function devtoolPlugin(store: Store<string, StateTree>) {
if (!devtoolHook) return
if (!rootStore) {
devtoolHook.emit('vuex:init', rootStore)
}
- rootStore.state[store.name] = store.state
+ rootStore.state[store.id] = store.state
// tell the devtools we added a module
- rootStore.registerModule(store.name, store)
+ rootStore.registerModule(store.id, store)
- Object.defineProperty(rootStore.state, store.name, {
+ Object.defineProperty(rootStore.state, store.id, {
get: () => store.state,
set: state => store.replaceState(state),
})
// Vue.set(rootStore.state, store.name, store.state)
// the trailing slash is removed by the devtools
- rootStore._modulesNamespaceMap[store.name + '/'] = true
+ rootStore._modulesNamespaceMap[store.id + '/'] = true
devtoolHook.on('vuex:travel-to-state', targetState => {
- store.replaceState(targetState[store.name] as S)
+ store.replaceState(targetState[store.id])
})
store.subscribe((mutation, state) => {
- rootStore.state[store.name] = state
+ rootStore.state[store.id] = state
devtoolHook.emit(
'vuex:mutation',
{
return target
}
-export function createStore<S extends StateTree>(
- name: string,
+/**
+ * NOTE: by allowing users to name stores correctly, they can nest them the way
+ * they want, no? like user/cart
+ */
+
+/**
+ * Creates a store instance
+ * @param id unique identifier of the store, like a name. eg: main, cart, user
+ * @param initialState initial state applied to the store, Must be correctly typed to infer typings
+ */
+
+export function createStore<Id extends string, S extends StateTree>(
+ id: Id,
initialState: S
// methods: Record<string | symbol, StoreMethod>
-): Store<S> {
+): Store<Id, S> {
const { state, replaceState } = createState(initialState)
let isListening = true
state => {
if (isListening) {
subscriptions.forEach(callback => {
- callback({ storeName: name, type: '🧩 in place', payload: {} }, state)
+ callback({ storeName: id, type: '🧩 in place', payload: {} }, state)
})
}
},
isListening = true
subscriptions.forEach(callback => {
callback(
- { storeName: name, type: '⤵️ patch', payload: partialState },
+ { storeName: id, type: '⤵️ patch', payload: partialState },
state.value
)
})
// TODO: return function to remove subscription
}
- const store: Store<S> = {
- name,
+ const store: Store<Id, S> = {
+ id,
// it is replaced below by a getter
state: state.value,
return store
}
+function makeStore<Id extends string, S extends StateTree>(
+ id: Id,
+ initialState: S
+) {
+ let store: Store<Id, S> | undefined
+
+ function useStore(): Store<Id, S> {
+ if (!store) store = createStore(id, initialState)
+
+ return store
+ }
+
+ function clear(): void {
+ store = undefined
+ }
+
+ return {
+ useStore,
+ clear,
+ }
+}
+
// export const store = createStore('main', initialState)
// export const cartStore = createStore('cart', {
// items: ['thing 1'],
state: S
) => void
-export interface Store<S extends StateTree> {
- name: string
+export interface Store<Id extends string, S extends StateTree> {
+ /**
+ * Unique identifier of the store
+ */
+ id: Id
+ /**
+ * State of the Store
+ */
state: S
+ /**
+ * Applies a state patch to current state. Allows passing nested values
+ * @param partialState patch to apply to the state
+ */
patch(partialState: DeepPartial<S>): void
+ /**
+ * Replaces current state with a completely new version.
+ * @param newState state object to replace current state
+ */
replaceState(newState: S): void
+ /**
+ * Setups a callback to be called whenever the state changes.
+ * @param callback callback that is called whenever the state changes
+ */
subscribe(callback: SubscriptionCallback<S>): void
}
export interface DevtoolHook {
- on(event: string, callback: (targetState: StateTree) => void): void
+ on(
+ event: string,
+ callback: (targetState: Record<string, StateTree>) => void
+ ): void
// eslint-disable-next-line @typescript-eslint/no-explicit-any
emit(event: string, ...payload: any[]): void
}