-
Notifications
You must be signed in to change notification settings - Fork 271
Commit
NEXT-40449
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,341 @@ | ||
--- | ||
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: | ||
Check warning on line 23 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L23
Raw output
|
||
|
||
**Before (Vuex):** | ||
|
||
```javascript | ||
export default { | ||
namespaced: true, | ||
|
||
state: { | ||
// Initial state | ||
... | ||
}, | ||
mutations: { | ||
... | ||
}, | ||
getters: { | ||
... | ||
}, | ||
actions: { | ||
... | ||
}, | ||
} | ||
``` | ||
|
||
**After (Pinia):** | ||
|
||
```javascript | ||
const store = Shopware.Store.register('<storeName>', { | ||
Check warning on line 50 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L50
Raw output
|
||
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({ | ||
Check warning on line 68 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L68
Raw output
|
||
id: '<storeName>', | ||
Check warning on line 69 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L69
Raw output
|
||
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('<storeName>'); | ||
Check warning on line 85 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L85
Raw output
|
||
``` | ||
|
||
- 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); | ||
Check warning on line 95 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L95
Raw output
|
||
``` | ||
|
||
**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. | ||
|
||
- **Mutations:** | ||
- Vuex `mutations` are no longer needed in Pinia, since you can modify state directly in actions or compute it dynamically. | ||
- If a mutation only sets the state to a passed value, remove it and update the state directly in your action. | ||
- If a mutation has extra business logic, convert it into an action. | ||
|
||
- **Actions:** | ||
- In Pinia, actions don't receive `state` as an argument. Instead, state is accessed directly using `this.anyStateField`. | ||
|
||
**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. | ||
- It is not necessary to create a getter to return a state property. You can access it directly from the store instance. | ||
- Avoid side effects in getters to prevent unexpected bugs. Getters should only 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. | ||
- To use the correct types when calling `Shopware.Store.get`, you can infer the store's type: | ||
Check warning on line 135 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L135
Raw output
|
||
|
||
```typescript | ||
const store = Shopware.Store.register({ | ||
Check warning on line 138 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L138
Raw output
|
||
id: 'myStore', | ||
Check warning on line 139 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L139
Raw output
|
||
... | ||
}); | ||
|
||
export type StoreType = ReturnType<typeof store>; | ||
``` | ||
|
||
- 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('<storeName>', function() { | ||
Check warning on line 163 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L163
Raw output
|
||
const count = ref(0); | ||
const doubled = computed(() => count.value * 2); | ||
Check warning on line 166 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L166
Raw output
|
||
function increment() { | ||
count.value++; | ||
Check warning on line 169 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L169
Raw output
|
||
} | ||
function decrement() { | ||
count.value--; | ||
Check warning on line 173 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L173
Raw output
|
||
} | ||
return { count, doubled, increment, decrement }; | ||
}); | ||
``` | ||
|
||
### Accessing the Store | ||
|
||
To access the store in Vuex, you would typically do: | ||
|
||
```javascript | ||
Shopware.State.get('<storeName>'); | ||
Check warning on line 185 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L185
Raw output
|
||
``` | ||
|
||
When migrating to Pinia, it changes to: | ||
|
||
```javascript | ||
Shopware.Store.get('<storeName>'); | ||
Check warning on line 191 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L191
Raw output
|
||
``` | ||
|
||
### Accessing State, Getters, Mutations, and Actions | ||
|
||
Below is a practical example of how store access and usage changes from Vuex to Pinia: | ||
|
||
**Before (Vuex):** | ||
|
||
```javascript | ||
// Get the store | ||
const store = Shopware.State.get('product'); | ||
Check warning on line 202 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L202
Raw output
|
||
// Access the state | ||
const productCount = Shopware.State.get('product').products.length; | ||
Check warning on line 205 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L205
Raw output
|
||
// Use getters | ||
Shopware.State.getters['product/productCount']; | ||
Check warning on line 208 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L208
Raw output
|
||
// Execute mutations | ||
store.commit('product/setProducts', [{ id: 1, name: 'Test' }]); | ||
Check warning on line 211 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L211
Raw output
|
||
// Execute actions | ||
store.dispatch('product/fetchProducts'); | ||
``` | ||
|
||
**After (Pinia):** | ||
|
||
```javascript | ||
// Get the store | ||
const store = Shopware.Store.get('product'); | ||
Check warning on line 221 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L221
Raw output
|
||
// Access the state directly | ||
const productCount = Shopware.Store.get('product').products.length; | ||
Check warning on line 224 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L224
Raw output
|
||
// Use getters (no 'get' prefix) | ||
const totalProducts = Shopware.Store.get('product').productCount; | ||
Check warning on line 227 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L227
Raw output
|
||
// Mutate the state directly or through an action | ||
Shopware.Store.get('product').products = [{ id: 1, name: 'Test' }]; | ||
Check warning on line 230 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L230
Raw output
Check warning on line 230 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L230
Raw output
|
||
// Execute actions (called like methods) | ||
Shopware.Store.get('product').fetchProducts(); | ||
Check warning on line 233 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L233
Raw output
|
||
``` | ||
|
||
In Pinia, you no longer need `commit` for mutations or `dispatch` for actions. | ||
Instead, you can work with the state and call actions directly. | ||
|
||
### MapState | ||
|
||
**Before (Vuex)** | ||
|
||
```javascript | ||
const { mapState } = Shopware.Component.getComponentHelper(); | ||
Check warning on line 244 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L244
Raw output
|
||
export default { | ||
computed: { | ||
...mapState('product', ['products', 'productsCount']), | ||
}, | ||
}; | ||
``` | ||
|
||
**After (Pinia)** (using the Composition API): | ||
|
||
```javascript | ||
const { mapState } = Shopware.Component.getComponentHelper(); | ||
Check warning on line 256 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L256
Raw output
|
||
export default { | ||
computed: { | ||
...mapState(() => Shopware.Store.get('product'), ['products', 'productsCount']), | ||
Check warning on line 260 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L260
Raw output
|
||
}, | ||
}; | ||
``` | ||
|
||
Remember to pass a function returning the store to `mapState`. `Shopware.Store.get` returns the store instance directly, while `mapStore` expects the `useStore` function. Also note that [mapGetters is deprecated](https://pinia.vuejs.org/api/pinia/functions/mapGetters.html), so you should rely on `mapState`. | ||
Check warning on line 265 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L265
Raw output
|
||
|
||
### 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'); | ||
Check warning on line 275 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L275
Raw output
|
||
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); | ||
}); | ||
}); | ||
``` | ||
|
||
Take into account that Pinia actions do not return a Promise. So to check DOM changes it might be necessary to await `nextTick()`; | ||
|
||
```javascript | ||
describe('MyComponent.vue', () => { | ||
let wrapper; | ||
const store = Shopware.Store.get('myStore'); | ||
Check warning on line 320 in guides/plugins/plugins/administration/system-updates/pinia.md GitHub Actions / LanguageTool[LanguageTool] guides/plugins/plugins/administration/system-updates/pinia.md#L320
Raw output
|
||
beforeEach(async () => { | ||
wrapper = mount(await wrapTestComponent('myComponent', { sync: true }), { | ||
global: { | ||
plugins: [createPinia], | ||
}, | ||
}); | ||
}); | ||
it('increments the counter when the button is clicked', async () => { | ||
store.increment(); | ||
// Wait for state updates and DOM reactivity | ||
await nextTick(); | ||
// Assert the state and the DOM | ||
expect(store.counter).toBe(1); // State change | ||
expect(wrapper.find('p').text()).toBe('Counter: 1'); // DOM update | ||
}); | ||
}); | ||
``` |