From e25a1bb82ea463a4ec07ca30b7d2aa7e626a7f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Tajes=20Vidal?= Date: Mon, 27 Jan 2025 16:35:16 +0100 Subject: [PATCH 1/2] doc: add upgrade information for Pinia migration --- .wordlist.txt | 2 + .../administration/system-updates/pinia.md | 270 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 guides/plugins/plugins/administration/system-updates/pinia.md diff --git a/.wordlist.txt b/.wordlist.txt index 3aabd3f05..23618167c 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -493,6 +493,7 @@ ManyToManyIdField ManyToOne ManyToOneAssociation ManyToOneAssociationField +MapState MediaDataSelection MediaDataSet MediaFolderDataSet @@ -1386,6 +1387,7 @@ mandatorily manufacturerId manytomanyid mapErrors +mapGetters martinfowler masternode matchers diff --git a/guides/plugins/plugins/administration/system-updates/pinia.md b/guides/plugins/plugins/administration/system-updates/pinia.md new file mode 100644 index 000000000..ad7a52f92 --- /dev/null +++ b/guides/plugins/plugins/administration/system-updates/pinia.md @@ -0,0 +1,270 @@ +--- +nav: + title: Upgrading to Pinia + position: 261 +--- + +# Migration from Vuex in Shopware to Pinia + +## Introduction + +With the release of Shopware 6.7, we will replace Vuex with [Pinia](https://pinia.vuejs.org/) as the state management library for the administration. + +## Why Pinia? + +Migrating to Pinia simplifies state management with an intuitive API, no need for mutations, better TypeScript support, and seamless integration with Vue 3 Composition API. It’s lightweight, modular, and offers modern features like devtools support, making it a more efficient alternative to Vuex. + +## Migration Guide + +To migrate a Vuex store to Pinia, you need to make some changes to the store definition and how you access it in components. + +- First, register it with `Shopware.Store.register` and define the store with `state`, `getters`, and `actions` properties: + +**Before (Vuex):** + +```javascript +export default { + namespaced: true, + + state: { + // Initial state + ... + }, + mutations: { + ... + }, + getters: { + ... + }, + actions: { + ... + }, +} +``` + +**After (Pinia):** + +```javascript +const store = Shopware.Store.register('', { + state: () => ({ + // Initial state + ... + }), + getters: { + ... + }, + actions: { + ... + }, +}); +export default store; +``` + +- You can also register the store with an `id` property in the definition object, for example: + +```javascript +const store = Shopware.Store.register({ + id: '', + state: () => ({ + // Initial state + }), + getters: { + // ... + }, + actions: { + // ... + }, +}); +``` + +- If you register a store that already exists, it will be overwritten. You can also unregister a store: + +```javascript +Shopware.Store.unregister(''); +``` + +- To register a store from a component or index file, simply import the store file. + +**Before (Vuex):** + +```javascript +import productsStore from './state/products.state'; + +Shopware.State.registerModule('product', productsStore); +``` + +**After (Pinia):** + +```javascript +import './state/products.state'; +``` + +### Key Changes + +- **State:** + + - In Pinia, `state` must be a function returning the initial state instead of a static object. + + - Example: + + ```javascript + state: () => ({ + productName: '', + }) + ``` + +- **Mutations:** + + - Vuex `mutations` are no longer needed in Pinia, since you can modify state directly in actions or compute it dynamically. + + - Example: + + ```javascript + actions: { + updateProductName(newName) { + this.productName = newName; // Directly update state + }, + }, + ``` + +- **Getters:** + + - There cannot be getters with the same name as a property in the state, as both are exposed at the same level in the store. + - Getters should be used to compute and return information based on state, without modifying it. + +- **TypeScript:** + + - We recommend migrating JavaScript stores to TypeScript for stricter typing, better autocompletion, and fewer errors during development. + - Example: + + ```typescript + const store = Shopware.Store.register({ + id: 'myStore', + ... + }); + + export type StoreType = ReturnType; + ``` + + Then, you can use this type to extend `PiniaRootState`: + + ```typescript + import type { StoreType } from './store/myStore'; + + declare global { + interface PiniaRootState { + myStore: StoreType; + } + } + ``` + +### Composables as a Store + +With Pinia, you can use reactive properties inside a store and define it like a composable. Keep in mind that only variables and functions returned from the store will be tracked by Pinia in devtools. + +```typescript +const store = Shopware.Store.register('', function() { + const count = ref(0); + + const doubled = computed(() => count.value * 2); + + function increment() { + count.value++; + } + + function decrement() { + count.value--; + } + + return { count, doubled, increment, decrement }; +}); +``` + +You can also use a composable function defined outside the store. This allows you to encapsulate and reuse logic across different stores or components, promoting better code organization and modularity: + +```typescript +// composables/myComposable.ts +export function useMyComposable() { + const count = ref(0); + + const doubled = computed(() => count.value * 2); + + function increment() { + count.value++; + } + + function decrement() { + count.value--; + } + + return { count, doubled, increment, decrement }; +} +``` + +```typescript +// store/myStore.ts +import { useMyComposable } from '../composables/myComposable'; + +const store = Shopware.Store.register('myStore', useMyComposable); +``` + +### Accessing the Store + +To access the store in Vuex, you would typically do: + +```javascript +Shopware.State.get(''); +``` + +When migrating to Pinia, it changes to: + +```javascript +Shopware.Store.get(''); +``` + +### Testing + +To test your store, just import it so it's registered. You can use `$reset()` to reset the store before each test: + +```javascript +import './store/my.store'; + +describe('my store', () => { + const store = Shopware.Store.get('myStore'); + + beforeEach(() => { + store.$reset(); + }); + + it('has initial state', () => { + expect(store.count).toBe(0); + }); +}); +``` + +When testing components that use Pinia stores, register Pinia as a plugin and reset it before each test: + +```javascript +import { createPinia, setActivePinia } from 'pinia'; + +const pinia = createPinia(); + +describe('my component', () => { + beforeEach(() => { + setActivePinia(pinia); + }); + + it('is a component', async () => { + const wrapper = mount(await wrapTestComponent('myComponent', { sync: true }), { + global: { + plugins: [pinia], + stubs: { + // ... + }, + }, + }); + + expect(wrapper.exists()).toBe(true); + }); +}); +``` From c6ef9f7678f61000bdc678e254d276190d8bcda3 Mon Sep 17 00:00:00 2001 From: Micha Date: Tue, 28 Jan 2025 10:00:14 +0100 Subject: [PATCH 2/2] Adjust styling and anchors --- .../administration/system-updates/pinia.md | 75 +++++++++---------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/guides/plugins/plugins/administration/system-updates/pinia.md b/guides/plugins/plugins/administration/system-updates/pinia.md index ad7a52f92..9e45a5d6a 100644 --- a/guides/plugins/plugins/administration/system-updates/pinia.md +++ b/guides/plugins/plugins/administration/system-updates/pinia.md @@ -101,62 +101,57 @@ import './state/products.state'; ### Key Changes -- **State:** +#### State - - In Pinia, `state` must be a function returning the initial state instead of a static object. +In Pinia, `state` must be a function returning the initial state instead of a static object. - - Example: + ```javascript + state: () => ({ + productName: '', + }) + ``` - ```javascript - state: () => ({ - productName: '', - }) - ``` +#### Mutations -- **Mutations:** +Vuex `mutations` are no longer needed in Pinia, since you can modify state directly in actions or compute it dynamically. - - Vuex `mutations` are no longer needed in Pinia, since you can modify state directly in actions or compute it dynamically. - - - Example: - - ```javascript - actions: { - updateProductName(newName) { - this.productName = newName; // Directly update state - }, +```javascript +actions: { + updateProductName(newName) { + this.productName = newName; // Directly update state }, - ``` +}, +``` -- **Getters:** +#### Getters - - There cannot be getters with the same name as a property in the state, as both are exposed at the same level in the store. - - Getters should be used to compute and return information based on state, without modifying it. +- There cannot be getters with the same name as a property in the state, as both are exposed at the same level in the store. +- Getters should be used to compute and return information based on state, without modifying it. -- **TypeScript:** +#### TypeScript - - We recommend migrating JavaScript stores to TypeScript for stricter typing, better autocompletion, and fewer errors during development. - - Example: +We recommend migrating JavaScript stores to TypeScript for stricter typing, better autocompletion, and fewer errors during development. - ```typescript - const store = Shopware.Store.register({ - id: 'myStore', - ... - }); +```typescript +const store = Shopware.Store.register({ + id: 'myStore', + ... +}); - export type StoreType = ReturnType; - ``` +export type StoreType = ReturnType; +``` - Then, you can use this type to extend `PiniaRootState`: +Then, you can use this type to extend `PiniaRootState`: - ```typescript - import type { StoreType } from './store/myStore'; +```typescript +import type { StoreType } from './store/myStore'; - declare global { - interface PiniaRootState { - myStore: StoreType; - } +declare global { + interface PiniaRootState { + myStore: StoreType; } - ``` +} +``` ### Composables as a Store