]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
feat(hmr): handle changes to setup store
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 15 Jul 2021 15:57:16 +0000 (17:57 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 19 Jul 2021 09:52:25 +0000 (11:52 +0200)
playground/src/views/CounterSetupStore.vue
playground/src/views/CounterStore.vue
src/devtools/plugin.ts
src/store.ts
src/types.ts

index b68ad5afb6210679f789436fd7e1c928ed493da2..c06656b65923647de6976cd7933e9be24533bd68 100644 (file)
@@ -5,7 +5,7 @@
 
   <h2>Counter Store</h2>
 
-  <p>Counter :{{ counter.n }}</p>
+  <p>Counter :{{ counter.n }}. Double: {{ counter.double }}</p>
 
   <p>
     Increment the Store <br />
index 60b5da960ef78926a0b3f49d8316dd039c3ad0fe..5d0b14eba190bfcdf6ac5e426bfeb8f592342c4c 100644 (file)
@@ -5,7 +5,7 @@
 
   <h2>Counter Store</h2>
 
-  <p>Counter :{{ counter.n }}</p>
+  <p>Counter :{{ counter.n }}. Double: {{ counter.double }}</p>
 
   <p>
     Increment the Store <br />
index 3f41560932f4b059898ba295a2f612587d8e5fc6..e8ff0886c419bcd68206f10a3470c14ed5b9308f 100644 (file)
@@ -417,22 +417,26 @@ export function devtoolsPlugin<
     return
   }
 
-  patchActionForGrouping(
-    // @ts-expect-error: can cast the store...
-    store,
-    Object.keys(options.actions)
-  )
-
-  const originalHotUpdate = store.hotUpdate
-
-  // Upgrade the HMR to also update the new actions
-  toRaw(store).hotUpdate = function (newStore) {
-    originalHotUpdate.apply(this, arguments as any)
+  // only wrap actions in option-defined stores as this technique relies on
+  // wrapping the context of the action with a proxy
+  if ('id' in options) {
     patchActionForGrouping(
       // @ts-expect-error: can cast the store...
       store,
-      Object.keys(newStore._hmrPayload.actions)
+      Object.keys(options.actions)
     )
+
+    const originalHotUpdate = store.hotUpdate
+
+    // Upgrade the HMR to also update the new actions
+    toRaw(store).hotUpdate = function (newStore) {
+      originalHotUpdate.apply(this, arguments as any)
+      patchActionForGrouping(
+        // @ts-expect-error: can cast the store...
+        store,
+        Object.keys(newStore._hmrPayload.actions)
+      )
+    }
   }
 
   addStoreToDevtools(
index 8ac71fb7222268897db635a76ce6d07b70f9ceab..748117961a34f3db83cf5a95d5a564f519d5d03d 100644 (file)
@@ -94,13 +94,19 @@ function createOptionsStore<
   const initialState: StateTree | undefined = pinia.state.value[id]
 
   function setup() {
-    if (!initialState) {
+    if (!initialState && (!__DEV__ || !hot)) {
       $reset()
     }
     // pinia.state.value[id] = state ? state() : {}
 
+    // avoid creating a state in pinia.state.value
+    const localState =
+      __DEV__ && hot
+        ? toRefs(ref(state ? state() : {}).value)
+        : initialState || toRefs(pinia.state.value[id])
+
     return assign(
-      initialState || toRefs(pinia.state.value[id]),
+      localState,
       actions,
       Object.keys(getters || {}).reduce((computedGetters, name) => {
         computedGetters[name] = computed(() => {
@@ -176,11 +182,13 @@ function createSetupStore<
   let debuggerEvents: DebuggerEvent[] | DebuggerEvent
   const initialState = pinia.state.value[$id] as UnwrapRef<S> | undefined
 
-  if (!initialState) {
+  if (!initialState && !hot) {
     // should be set in Vue 2
     pinia.state.value[$id] = {}
   }
 
+  const hotState = ref({} as S)
+
   const triggerSubscriptions: SubscriptionCallback<S> = (mutation, state) => {
     subscriptions.forEach((callback) => {
       callback(mutation, state)
@@ -310,7 +318,7 @@ function createSetupStore<
   }
 
   /**
-   * Wraps an action to handle subscriptions
+   * Wraps an action to handle subscriptions.
    *
    * @param name - name of the action
    * @param action - action to wrap
@@ -359,6 +367,7 @@ function createSetupStore<
     actions: {} as Record<string, any>,
     getters: {} as Record<string, Ref>,
     state: [] as string[],
+    hotState,
   })
 
   // overwrite existing actions to support $onAction
@@ -366,11 +375,14 @@ function createSetupStore<
     const prop = setupStore[key]
 
     if ((isRef(prop) && !isComputed(prop)) || isReactive(prop)) {
-      // createOptionStore already did this
-      if (!buildState) {
-        // mark it as a piece of state to be serialized
+      // mark it as a piece of state to be serialized
+      if (__DEV__ && hot) {
+        hotState.value[key] = toRef(setupStore as any, key)
+        // createOptionStore already did this
+      } else if (!buildState) {
         pinia.state.value[$id][key] = toRef(setupStore as any, key)
       }
+
       if (__DEV__) {
         _hmrPayload.state.push(key)
       }
@@ -431,28 +443,41 @@ function createSetupStore<
   // without linking the computed lifespan to wherever the store is first
   // created.
   Object.defineProperty(store, '$state', {
-    get: () => pinia.state.value[$id],
-    set: (state) => (pinia.state.value[$id] = state),
+    get: () => (__DEV__ && hot ? hotState.value : pinia.state.value[$id]),
+    set: (state) => {
+      if (__DEV__ && hot) {
+        throw new Error('cannot set hotState')
+      }
+      pinia.state.value[$id] = state
+    },
   })
 
   // add the hotUpdate before plugins to allow them to override it
   if (__DEV__) {
     store.hotUpdate = markRaw((newStore) => {
       newStore._hmrPayload.state.forEach((stateKey) => {
-        if (!(stateKey in store.$state)) {
-          console.log('setting new key', stateKey)
+        if (stateKey in store.$state) {
           // @ts-expect-error
           // transfer the ref
-          store.$state[stateKey] = newStore.$state[stateKey]
+          newStore.$state[stateKey] =
+            // ---
+            // @ts-expect-error
+            store.$state[stateKey]
+
+          // patch direct access properties to allow store.stateProperty to work as
+          // store.$state.stateProperty
+          // @ts-expect-error
+          store[stateKey] = toRef(newStore.$state, stateKey)
         }
       })
 
+      pinia.state.value[$id] = toRef(newStore._hmrPayload, 'hotState')
+
       // remove deleted keys
       Object.keys(store.$state).forEach((stateKey) => {
         if (!(stateKey in newStore.$state)) {
-          console.log('deleting old key', stateKey)
           // @ts-expect-error
-          delete store.$state[stateKey]
+          delete store[stateKey]
         }
       })
 
index 45883dc5a7bc5b442b9685562b4041980f9588df..6c665a71225dd15ae88ff75133cedc41c32f4034 100644 (file)
@@ -1,4 +1,4 @@
-import { DebuggerEvent, UnwrapRef } from 'vue'
+import { DebuggerEvent, Ref, UnwrapRef } from 'vue'
 import { Pinia } from './rootStore'
 
 /**
@@ -265,6 +265,7 @@ export interface StoreWithState<
    */
   _hmrPayload: {
     state: string[]
+    hotState: Ref<S>
     actions: ActionsTree
     getters: ActionsTree
   }