]> git.ipfire.org Git - thirdparty/vuejs/pinia.git/commitdiff
refactor: rename createStore to defineStore
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 28 Sep 2020 13:58:25 +0000 (15:58 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 28 Sep 2020 13:58:28 +0000 (15:58 +0200)
BREAKING CHANGE: renamed `createStore` to `defineStore`. `createStore`
will be marked as deprecated during the alpha releases and then be
dropped.

13 files changed:
README.md
__tests__/actions.spec.ts
__tests__/getters.spec.ts
__tests__/pinia/stores/cart.ts
__tests__/pinia/stores/user.ts
__tests__/rootState.spec.ts
__tests__/state.spec.ts
__tests__/store.patch.spec.ts
__tests__/store.spec.ts
__tests__/subscriptions.spec.ts
package.json
test-dts/store.test-d.ts
yarn.lock

index 969b0ffd4f9370455baeb41ee62ad0994840610b..f1f28be629122e33643cd0cc534344235deb6b26 100644 (file)
--- a/README.md
+++ b/README.md
@@ -58,7 +58,8 @@ A few notes about the project and possible questions:
 ## Roadmap / Ideas
 
 - [x] Should the state be merged at the same level as actions and getters?
-- [ ] Allow grouping stores together into a similar structure and allow defining new getters (`pinia`)
+- [ ] ~~Allow grouping stores together into a similar structure and allow defining new getters (`pinia`)~~
+      You can directly call `useOtherStore()` inside of a getter or action.
 - [ ] Getter with params that act like computed properties (@ktsn)
 
 ## Installation
@@ -82,16 +83,16 @@ app.use(createPinia())
 ```
 
 This will also add devtools support.
-**NOTE**: this API is still experimental and is currently only used for devtools support but that might change in the future
+**NOTE**: this API is still experimental and is currently only used for devtools and SSR but that might change in the future.
 
 ### Creating a Store
 
 You can create as many stores as you want, and they should each exist in different files:
 
 ```ts
-import { createStore } from 'pinia'
+import { defineStore } from 'pinia'
 
-export const useMainStore = createStore({
+export const useMainStore = defineStore({
   // name of the store
   // it is used in devtools and allows restoring state
   id: 'main',
@@ -120,7 +121,7 @@ export const useMainStore = createStore({
 })
 ```
 
-`createStore` returns a function that has to be called to get access to the store:
+`defineStore` returns a function that has to be called to get access to the store:
 
 ```ts
 import { useMainStore } from '@/stores/main'
@@ -141,9 +142,9 @@ export default defineComponent({
 })
 ```
 
-Note: the SSR implementation is yet to be decided on Pinia, but if you intend having SSR on your application, you should avoid using `useStore` functions at the root level of a file to make sure the correct store is retrieved for your request.
+Note: the SSR implementation is yet to be decided on Pinia, but if you intend having SSR on your application, you should avoid using `useStore` functions at the root level of a file to make sure the correct store is retrieved for your request. Here is an example:
 
-Or:
+**Avoid doing this\***:
 
 ```ts
 import { createRouter } from 'vue-router'
@@ -155,12 +156,12 @@ const router = createRouter({
 const main = useMainStore()
 
 router.beforeEach((to, from, next) => {
-  if (main.state.isLoggedIn) next()
+  if (main.isLoggedIn) next()
   else next('/login')
 })
 ```
 
-It must be called **after the Composition API plugin is installed**. That's why calling `useStore` inside functions is usually safe, because they are called after the plugin being installed:
+Instead, call `useMainStore()` at the top of `setup`, like `inject` and `provide` in Vue:
 
 ```ts
 export default defineComponent({
@@ -174,12 +175,11 @@ export default defineComponent({
 
 // In a different file...
 
-router.beforeEach((to, from, next) => {
+router.beforeEach((to) => {
   // ✅ This will work (requires an extra param for SSR, see below)
   const main = useMainStore()
 
-  if (main.state.isLoggedIn) next()
-  else next('/login')
+  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
 })
 ```
 
@@ -191,9 +191,13 @@ You can access any property defined in `state` and `getters` directly on the sto
 export default defineComponent({
   setup() {
     const main = useMainStore()
-    const text = main.name
-    const doubleCount = main.doubleCount
-    return {}
+    const text = main.name // "eduardo"
+    const doubleCount = main.doubleCount // 2
+
+    return {
+      text, // will always be "eduardo"
+      textDynamic: computed(() => main.name), // reactive value
+    }
   },
 })
 ```
@@ -242,7 +246,7 @@ main.patch({
 })
 ```
 
-The main difference here is that `patch` allows you to group multiple changes into one single entry in the devtools (which are not yet available for Vue 3.x).
+The main difference here is that `patch` allows you to group multiple changes into one single entry in the devtools.
 
 ### Replacing the `state`
 
@@ -254,7 +258,34 @@ main.state = { counter: 666, name: 'Paimon' }
 
 ### SSR
 
-To be decided once SSR is implemented on Vue 3
+Pinia should work out of the box for SSR as long as you call your `useStore()` functions at the top of `setup` functions, `getters` and `actions`:
+
+```ts
+export default defineComponent({
+  setup() {
+    // this works because pinia knows what application is running
+    const main = useMainStore()
+    return { main }
+  },
+})
+```
+
+If you need to use the store somewhere else, you need to pass the `pinia` instance [that was passed to the app](#install-the-plugin) to the `useStore()` function call:
+
+```ts
+const pinia = createPinia()
+const app = createApp(App)
+
+app.use(router)
+app.use(pinia)
+
+router.beforeEach((to) => {
+  // ✅ This will work make sure the correct store is used for the current running app
+  const main = useMainStore(pinia)
+
+  if (to.meta.requiresAuth && !main.isLoggedIn) return '/login'
+})
+```
 
 ### Composing Stores
 
@@ -264,17 +295,43 @@ If **multiple stores use each other** or you need to use **multiple stores** at
 
 If one store uses an other store, there is no need to create a new file, you can directly import it. Think of it as nesting.
 
+You can call `useOtherStore()` at the top of any getter an action:
+
+```ts
+import { useUserStore } from './user'
+
+export const cartStore = defineStore({
+  id: 'cart',
+  getters: {
+    // ... other getters
+    summary() {
+      const user = useUserStore()
+
+      return `Hi ${user.name}, you have ${this.list.length} items in your cart. It costs ${this.price}.`
+    },
+  },
+
+  actions: {
+    purchase() {
+      const user = useUserStore()
+
+      return apiPurchase(user.id, this.list)
+    },
+  },
+})
+```
+
 #### Shared Getters
 
 If you need to compute a value based on the `state` and/or `getters` of multiple stores, you may be able to import all the stores but one into the remaining store, but depending on how your stores are used across your application, **this would hurt your code splitting** because importing the store that imports all others stores, would result in **one single big chunk** with all of your stores.
 To prevent this, **we follow the rule above** and we create a new file with a new store:
 
 ```ts
-import { createStore } from 'pinia'
+import { defineStore } from 'pinia'
 import { useUserStore } from './user'
 import { useCartStore } from './cart'
 
-export const useSharedStore = createStore({
+export const useSharedStore = defineStore({
   id: 'shared',
   getters: {
     summary() {
@@ -292,11 +349,11 @@ export const useSharedStore = createStore({
 When an actions needs to use multiple stores, we do the same, we create a new file with a new store:
 
 ```ts
-import { createStore } from 'pinia'
+import { defineStore } from 'pinia'
 import { useUserStore } from './user'
 import { useCartStore } from './cart'
 
-export const useSharedStore = createStore({
+export const useSharedStore = defineStore({
   id: 'shared',
   state: () => ({}),
   actions: {
@@ -315,42 +372,6 @@ export const useSharedStore = createStore({
 })
 ```
 
-#### Creating _Pinias_
-
-_Not implemented_. Still under discussion, needs more feedback as this doesn't seem necessary because it can be replaced by shared stores as shown above.
-
-Combine multiple _stores_ (gajos) into a new one:
-
-```ts
-import { pinia } from 'pinia'
-import { useUserStore } from './user'
-import { useCartStore, emptyCart } from './cart'
-
-export const useCartUserStore = pinia(
-  {
-    user: useUserStore,
-    cart: useCartStore,
-  },
-  {
-    getters: {
-      combinedGetter () {
-        return `Hi ${this.user.name}, you have ${this.cart.list.length} items in your cart. It costs ${this.cart.price}.`,
-      }
-    },
-    actions: {
-      async orderCart() {
-        try {
-          await apiOrderCart(this.user.token, this.cart.items)
-          this.cart.emptyCart()
-        } catch (err) {
-          displayError(err)
-        }
-      },
-    },
-  }
-)
-```
-
 ## Related
 
 ## License
index fb7aee81687633b93a9b1b58c16578f6b3cc5f29..a97a7227c3d7a6cd786b8513256669c21ccd5eed 100644 (file)
@@ -1,10 +1,10 @@
-import { createStore, setActiveReq } from '../src'
+import { defineStore, setActiveReq } from '../src'
 
 describe('Actions', () => {
   const useStore = () => {
     // create a new store
     setActiveReq({})
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         a: true,
@@ -41,12 +41,12 @@ describe('Actions', () => {
     })()
   }
 
-  const useB = createStore({
+  const useB = defineStore({
     id: 'B',
     state: () => ({ b: 'b' }),
   })
 
-  const useA = createStore({
+  const useA = defineStore({
     id: 'A',
     state: () => ({ a: 'a' }),
     actions: {
index 4b337a88aa13f8d0576cbb8bb1012540f73812e6..29aac184eebd41e2edc378921e8e5ec543c74099 100644 (file)
@@ -1,10 +1,10 @@
-import { createStore, setActiveReq } from '../src'
+import { defineStore, setActiveReq } from '../src'
 
 describe('Getters', () => {
   const useStore = () => {
     // create a new store
     setActiveReq({})
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         name: 'Eduardo',
@@ -23,12 +23,12 @@ describe('Getters', () => {
     })()
   }
 
-  const useB = createStore({
+  const useB = defineStore({
     id: 'B',
     state: () => ({ b: 'b' }),
   })
 
-  const useA = createStore({
+  const useA = defineStore({
     id: 'A',
     state: () => ({ a: 'a' }),
     getters: {
index 241991b797b26ea6ac5d16aefc60c9e01e1f429e..f7bd76c13853f7f4efeb50e412b44c7c08edea29 100644 (file)
@@ -1,7 +1,7 @@
-import { createStore } from '../../../src'
+import { defineStore } from '../../../src'
 import { useUserStore } from './user'
 
-export const useCartStore = createStore({
+export const useCartStore = defineStore({
   id: 'cart',
   state: () => ({
     rawItems: [] as string[],
@@ -21,6 +21,29 @@ export const useCartStore = createStore({
       }, [] as { name: string; amount: number }[])
     },
   },
+  actions: {
+    addItem(name: string) {
+      this.rawItems.push(name)
+    },
+
+    removeItem(name: string) {
+      const i = this.rawItems.indexOf(name)
+      if (i > -1) this.rawItems.splice(i, 1)
+    },
+
+    // TODO: use multiple stores
+    // https://github.com/vuejs/vue-next-internal-discussions/issues/22
+    async purchaseItems() {
+      const user = useUserStore()
+      if (!user.name) return
+
+      // console.log('Purchasing', this.items)
+      const n = this.items.length
+      this.state.rawItems = []
+
+      return { amount: n, user: user.name }
+    },
+  },
 })
 
 export type CartStore = ReturnType<typeof useCartStore>
index 52f9a1f30f39d12a76c7d589a0d8d09128b4d601..79c07d00c9e269c238e06dcf72b767a0a448e979 100644 (file)
@@ -1,11 +1,11 @@
-import { createStore } from 'src/store'
+import { defineStore } from '../../../src'
 
 function apiLogin(a: string, p: string) {
   if (a === 'ed' && p === 'ed') return Promise.resolve({ isAdmin: true })
   return Promise.reject(new Error('invalid credentials'))
 }
 
-export const useUserStore = createStore({
+export const useUserStore = defineStore({
   id: 'user',
   state: () => ({
     name: 'Eduardo',
index d3ee12f79a8ca6cc35b52da33176213a4c229156..5c9519ae67bd314ee00d95b4c99c5f89c2c2368c 100644 (file)
@@ -1,12 +1,12 @@
-import { createStore, getRootState } from '../src'
+import { defineStore, getRootState } from '../src'
 
 describe('Root State', () => {
-  const useA = createStore({
+  const useA = defineStore({
     id: 'a',
     state: () => ({ a: 'a' }),
   })
 
-  const useB = createStore({
+  const useB = defineStore({
     id: 'b',
     state: () => ({ b: 'b' }),
   })
index 7b6599c9d52a6a0dc113e37a02a97347bd1cc653..3a3ba4978bf90648d8967a8c9f8102c786069303 100644 (file)
@@ -1,11 +1,11 @@
-import { createStore, setActiveReq } from '../src'
+import { defineStore, setActiveReq } from '../src'
 import { computed } from 'vue'
 
 describe('State', () => {
   const useStore = () => {
     // create a new store
     setActiveReq({})
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         name: 'Eduardo',
index 90352926d7e17c260a8a95ae0ad299d5edfe3415..a8927f38bd12ae3c05fb701621cb93ec1855e255 100644 (file)
@@ -1,10 +1,10 @@
-import { createStore, setActiveReq } from '../src'
+import { defineStore, setActiveReq } from '../src'
 
 describe('store.patch', () => {
   const useStore = () => {
     // create a new store
     setActiveReq({})
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         a: true,
index d8cf1afbc53eef9005c6c3a0740a2e2d7e40b484..9b1b0367c3d802dd0328902ca31152b4d1b6c473 100644 (file)
@@ -1,4 +1,4 @@
-import { createStore, setActiveReq, setStateProvider } from '../src'
+import { defineStore, setActiveReq, setStateProvider } from '../src'
 
 describe('Store', () => {
   let req: object
@@ -6,7 +6,7 @@ describe('Store', () => {
     // create a new store
     req = {}
     setActiveReq(req)
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         a: true,
@@ -47,14 +47,14 @@ describe('Store', () => {
   })
 
   it('can create an empty state if no state option is provided', () => {
-    const store = createStore({ id: 'some' })()
+    const store = defineStore({ id: 'some' })()
 
     expect(store.state).toEqual({})
   })
 
   it('can hydrate the state', () => {
     setActiveReq({})
-    const useStore = createStore({
+    const useStore = defineStore({
       id: 'main',
       state: () => ({
         a: true,
index bdf393eff0deff53f7e4e03357f60a22f47bdb69..3b426c69fb9d639845afc3acccaa35f3cce31410 100644 (file)
@@ -1,10 +1,10 @@
-import { createStore, setActiveReq } from '../src'
+import { defineStore, setActiveReq } from '../src'
 
 describe('Subscriptions', () => {
   const useStore = () => {
     // create a new store
     setActiveReq({})
-    return createStore({
+    return defineStore({
       id: 'main',
       state: () => ({
         name: 'Eduardo',
index 6e66f0708bdf427c4ff1cf0456bd51a2c7b45127..0a3e9952c04b03b50a6da68bcb9cc379bbe9138f 100644 (file)
@@ -58,6 +58,7 @@
     "@types/jest": "^26.0.14",
     "@types/node": "^14.11.2",
     "@vue/devtools-api": "^6.0.0-beta.2",
+    "@vue/server-renderer": "^3.0.0",
     "brotli": "^1.3.2",
     "codecov": "^3.6.1",
     "conventional-changelog-cli": "^2.1.0",
index 943e8c03c3953827bc5343b64b32248e54e47f51..57d1ba5a903c8df312aaf9446b05c20b6f8c1d23 100644 (file)
@@ -1,6 +1,6 @@
-import { createStore, expectType } from './'
+import { defineStore, expectType } from './'
 
-const useStore = createStore({
+const useStore = defineStore({
   id: 'name',
   state: () => ({ a: 'on' as 'on' | 'off', nested: { counter: 0 } }),
   getters: {
index 2ee6f96229a206cd874f44e05402ccb11ba0048b..66a4e07c77c3bbabec769b4e2438677bf5141957 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
     "@vue/compiler-core" "3.0.0"
     "@vue/shared" "3.0.0"
 
+"@vue/compiler-ssr@3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.0.tgz#d717abcd23a89fb38d1497228633a21bcf9a0e28"
+  integrity sha512-Er41F9ZFyKB3YnNbE6JSTIGCVWve3NAQimgDOk4uP42OnckxBYKGBTutDeFNeqUZBMu/9vRHYrxlGFC9Z5jBVQ==
+  dependencies:
+    "@vue/compiler-dom" "3.0.0"
+    "@vue/shared" "3.0.0"
+
 "@vue/devtools-api@^6.0.0-beta.2":
   version "6.0.0-beta.2"
   resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.0.0-beta.2.tgz#833ad3335f97ae9439e26247d97f9baf7b5a6116"
     "@vue/shared" "3.0.0"
     csstype "^2.6.8"
 
+"@vue/server-renderer@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.0.0.tgz#d531f62f64b67481aae32034eb003a16941b1a52"
+  integrity sha512-Ft4kiymPdI8BOYMTVSwB2bUeVH/PsnuEc6s7i9SzQxbz5JZpzzK8ugzICxtSYc8WkNaqi0J1xqdXz2df/YXAqQ==
+  dependencies:
+    "@vue/compiler-ssr" "3.0.0"
+    "@vue/shared" "3.0.0"
+
 "@vue/shared@3.0.0":
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.0.tgz#ec089236629ecc0f10346b92f101ff4339169f1a"