text: 'Composing Stores',
link: '/cookbook/composing-stores.html',
},
+ {
+ text: 'Usage without setup()',
+ link: '/cookbook/options-api.html',
+ },
],
},
],
--- /dev/null
+# Usage without `setup()`
+
+Pinia can be used even if you are not using the composition API (if you are using Vue 2, you still need to install the `@vue/composition-api` plugin though). While we recommend you to give the Composition API a try and learn it, it might not be the time for you and your team yet, you might be in the process of migrating an application, or any other reason. There are a few functions:
+
+- [mapStores](#giving-access-to-the-whole-store)
+- [mapState](../core-concepts/state.md#options-api)
+- [mapWritableState](../core-concepts/state.md#modifiable-state)
+- ⚠️ [mapGetters](../core-concepts/getters.md#options-api) (just for migration convenience, use `mapState()` instead)
+- [mapActions](../core-concepts/actions.md#options-api)
+
+## Giving access to the whole store
+
+If you need to access pretty much everything from the store, it might be too much to map every single property of the store... Instead you can get access to the whole store with `mapStores()`:
+
+```js
+import { mapStores } from 'pinia'
+
+// given two stores with the following ids
+const useUserStore = defineStore({ id: 'user' })
+const useCartStore = defineStore({ id: 'cart' })
+
+export default {
+ computed: {
+ // note we are not passing an array, just one store after the other
+ // each store will be accessible as its id + 'Store'
+ ...mapStores(useCartStore, useUserStore),
+ }),
+ },
+
+ methods: {
+ async buyStuff() {
+ // use them anywhere!
+ if (this.userStore.isAuthenticated()) {
+ await this.cartStore.buy()
+ this.$router.push('/purchased')
+ }
+ },
+ },
+}
+```
+
+By default, Pinia will add the `"Store"` suffix to the `id` of each store. You can customize this behavior by calling the `setMapStoreSuffix()`:
+
+```js
+import { createPinia, setMapStoreSuffix } from 'pinia'
+
+setMapStoreSuffix('') // completely remove the suffix
+setMapStoreSuffix('_store') // user_store, cart_store (it's okay, I won't judge you)
+export const pinia = createPinia()
+```
+
+## TypeScript
+
+By default, all map helpers support autocompletion and you don't need to do anything. If you call `setMapStoreSuffix()` to change the `"Store"` suffix, you will need to also add it somewhere in a TS file or your `global.d.ts` file. The most convenient place would be the same place where you call `setMapStoreSuffix()`:
+
+```ts
+import { createPinia, setMapStoreSuffix } from 'pinia'
+
+setMapStoreSuffix('') // completely remove the suffix
+export const pinia = createPinia()
+
+declare module 'pinia' {
+ export interface MapStoresCustomization {
+ // set it to the same value as above
+ suffix: ''
+ }
+}
+```
counter: 0,
}),
actions: {
+ increment() {
+ this.counter++
+ },
randomizeCounter() {
this.counter = Math.round(100 * Math.random())
},
},
})
```
+
+## Usage with `setup()`
+
+You can directly call any action as a method of the store:
+
+```js
+export default {
+ setup() {
+ const store = useStore()
+
+ store.randomzieCounter()
+ },
+}
+```
+
+## Usage with options API
+
+If you are not using the composition API, and you are using `computed`, `methods`, ..., you can use the `mapActions()` helper to map actions properties as methods in your component:
+
+```js
+import { mapActions } from 'pinia'
+
+export default {
+ computed: {
+ // gives access to this.increment() inside the component
+ // same as calling from store.increment()
+ ...mapActions(useStore, ['increment'])
+ // same as above but registers it as this.myOwnName()
+ ...mapActions(useStore, { myOwnName: 'doubleCounter' }),
+ },
+}
+```
},
})
```
+
+## Usage with `setup()`
+
+You can directly access any getter as a property of the store (exactly like state properties):
+
+```js
+export default {
+ setup() {
+ const store = useStore()
+
+ store.counter = 3
+ store.doubleCount // 6
+ },
+}
+```
+
+## Options API
+
+You can use the same `mapState()` function used in the [previous section of state](./state.md#options-api) to map to getters:
+
+```js
+import { mapState } from 'pinia'
+
+export default {
+ computed: {
+ // gives access to this.doubleCounter inside the component
+ // same as reading from store.doubleCounter
+ ...mapState(useStore, ['doubleCount'])
+ // same as above but registers it as this.myOwnName
+ ...mapState(useStore, {
+ myOwnName: 'doubleCounter',
+ // you can also write a function that gets access to the store
+ double: store => store.doubleCount,
+ }),
+ },
+}
+```
store.counter++
```
+### Options API
+
+If you are not using the composition API, and you are using `computed`, `methods`, ..., you can use the `mapState()` helper to map state properties as readonly computed properties:
+
+```js
+import { mapState } from 'pinia'
+
+export default {
+ computed: {
+ // gives access to this.counter inside the component
+ // same as reading from store.counter
+ ...mapState(useStore, ['counter'])
+ // same as above but registers it as this.myOwnName
+ ...mapState(useStore, {
+ myOwnName: 'counter',
+ // you can also write a function that gets access to the store
+ double: store => store.counter * 2,
+ // it can have access to `this` but it won't be typed correctly...
+ magicValue(store) {
+ return store.someGetter + this.counter + this.double
+ },
+ }),
+ },
+}
+```
+
+#### Modifiable state
+
+If you want to be able to write to these state properties (e.g. if you have a form), you can use `mapWritableState()` instead. Note you cannot pass a function like with `mapState()`:
+
+```js
+import { mapWritableState } from 'pinia'
+
+export default {
+ computed: {
+ // gives access to this.counter inside the component and allows setting it
+ // this.counter++
+ // same as reading from store.counter
+ ...mapWritableState(useStore, ['counter'])
+ // same as above but registers it as this.myOwnName
+ ...mapWritableState(useStore, {
+ myOwnName: 'counter',
+ }),
+ },
+}
+```
+
+:::tip
+You don't need `mapWritableState()` for collections like arrays unless you are replacing the whole array with `cartItems = []`, `mapState()` still allows you to call methods on your collections.
+:::
+
+## Mutating the state
+
<!-- TODO: disable this with `strictMode` -->
-or call the method `$patch` that allows you apply multiple changes at the same time with a partial `state` object:
+Apart from directly changing a the store with `store.counter++`, you can also call the `$patch` method. It allows you to apply multiple changes at the same time with a partial `state` object:
```js
store.$patch({
})
```
-The `$patch` method also accepts a function to group object mutations that are difficult to apply with an object, e.g. array mutations:
+However, some mutations are really hard or costly to apply with this syntax: any collection modification (e.g. pushing, removing, splicing an element from an array) requires you to create a new collection. Because of this, the `$patch` method also accepts a function to group this kind of mutations that are difficult to apply with a patch object:
```js
cartStore.$patch((state) => {
```js
store.$state = { counter: 666, name: 'Paimon' }
```
+
+You can also replace the whole state of your application by changing the `state` of the `pinia` instance. This is used during [SSR for hydration](http://localhost:3000/ssr/#state-hydration).
+
+```js
+pinia.state.value = {}
+```
}
```
+You will find more information about each _map helper_ in the core concepts.
+
## Why _Pinia_
Pinia (pronounced like `/peenya/` in English) is the closest word to _piña_ (_pineapple_ in Spanish) that is a valid package name. A pineapple is in reality a group of individual flowers that join together to create a multiple fruit. Similar to stores, each one is born individually, but they are all connected at the end. It's also a delicious tropical fruit indigenous to South America.
const computedStores = mapStores(useStore, useStoreDos, useCounter)
+// @ts-expect-error: no array
+mapStores([useStore, useStoreDos, useCounter])
+
expectType<{
nameStore: () => MainStore
dosStore: () => DosStore