Skip to content

Commit f30bad3

Browse files
author
Damiano
committed
Fixed some imports to avoid circular references, and improved the way we export and consume the Vuex store to be able to use strongly-typed code and avoid the ugly string interpolation for moduleName/mutationName throughout the code (see Chapter 9 as it has been updated as well in the book)
1 parent 2a0f145 commit f30bad3

File tree

18 files changed

+134
-48
lines changed

18 files changed

+134
-48
lines changed

Diff for: composition-api/src/App.vue

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
<script lang="ts">
1616
import { defineComponent, computed } from 'vue'
1717
import { useI18n } from 'vue-i18n'
18-
import { MutationType, StoreModuleNames } from '@/models/store'
19-
import { store } from '@/store'
18+
import { MutationType } from '@/models/store'
19+
import { useLocalesStore } from '@/store'
2020
import { config } from '@/config'
2121
import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
2222
import LocaleSelector from '@/components/locale-selector/LocaleSelector.component.vue'
@@ -30,16 +30,17 @@
3030
},
3131
setup() {
3232
const i18n = useI18n()
33+
const localesStore = useLocalesStore()
3334
3435
const availableLocales = computed(() => {
35-
return store.state.localesState.availableLocales
36+
return localesStore.state.availableLocales
3637
})
3738
3839
const availableThemes = config.themes
3940
4041
const onLocaleClicked = (localeInfo: LocaleInfoInterface) => {
41-
store.dispatch(
42-
`${StoreModuleNames.localesState}/${MutationType.locales.selectLocale}`,
42+
localesStore.action(
43+
MutationType.locales.selectLocale,
4344
localeInfo.locale
4445
)
4546
}

Diff for: composition-api/src/api-client/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
2-
import apiMockClient from '@/api-client/mock'
3-
import apiLiveClient from '@/api-client/live'
2+
import apiMockClient from './mock'
3+
import apiLiveClient from './live'
44
import { config } from '@/config'
55

66
// return either the live or the mock client

Diff for: composition-api/src/api-client/live/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
2-
import itemsApiClient from '@/api-client/live/items'
2+
import itemsApiClient from './items'
33

44
// create an instance of our main ApiClient that wraps the live child clients
55
const apiLiveClient: ApiClientInterface = {

Diff for: composition-api/src/api-client/mock/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ApiClientInterface } from '@/models/api-client/ApiClient.interface'
2-
import itemsApiClient from '@/api-client/mock/items'
2+
import itemsApiClient from './items'
33

44
// create an instance of our main ApiClient that wraps the mock child clients
55
const apiMockClient: ApiClientInterface = {

Diff for: composition-api/src/components/items/ItemsList.component.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<script lang="ts">
1212
import { defineComponent, PropType } from 'vue'
1313
import { ItemInterface } from '@/models/items/Item.interface'
14-
import ItemComponent from '@/components/items/children/Item.component.vue'
14+
import ItemComponent from './children/Item.component.vue'
1515
import Loader from '@/components/shared/Loader.component.vue'
1616
import { useI18n } from 'vue-i18n'
1717

Diff for: composition-api/src/main.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { createApp } from 'vue'
22
import App from './App.vue'
33
import router from './router'
44
import { store } from './store'
5-
import { i18n } from '@/plugins/vue-i18n-next-plugin'
6-
import { MyAppScss } from '@/plugins/myapp-scss'
7-
import { FlagIconsScss } from '@/plugins/flags-icons/'
5+
import { i18n } from './plugins/vue-i18n-next-plugin'
6+
import { MyAppScss } from './plugins/myapp-scss'
7+
import { FlagIconsScss } from './plugins/flags-icons/'
88

99
createApp(App)
1010
.use(store)

Diff for: composition-api/src/models/api-client/ApiClient.interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// GEN-IMPORTS
2-
import { ItemsApiClientInterface } from './api-client/items'
2+
import { ItemsApiClientInterface } from './items'
33

44
/**
55
* @Name ApiClientInterface

Diff for: composition-api/src/models/http-client/HttpClient.model.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios'
1+
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
22
import { HttpRequestParamsInterface } from './HttpRequestParams.interface'
33
import { HttpClientInterface } from './HttpClient.interface'
44
import { config } from '@/config'
@@ -37,12 +37,12 @@ export class HttpClientModel implements HttpClientInterface {
3737

3838
axios
3939
.get(url, options)
40-
.then((response: any) => {
40+
.then((response: AxiosResponse) => {
4141
resolve(response.data as T)
4242
})
43-
.catch((response: any) => {
43+
.catch((error: AxiosResponse) => {
4444
console.info('------ rejecting ----')
45-
reject(response)
45+
reject(error)
4646
})
4747
})
4848
}
@@ -63,11 +63,11 @@ export class HttpClientModel implements HttpClientInterface {
6363

6464
axios
6565
.post(url, payload, options)
66-
.then((response: any) => {
66+
.then((response: AxiosResponse) => {
6767
resolve(response.data as T)
6868
})
69-
.catch((response: any) => {
70-
reject(response)
69+
.catch((error: AxiosResponse) => {
70+
reject(error)
7171
})
7272
})
7373
}

Diff for: composition-api/src/models/store/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export * from './root/RootState.interface'
77
export * from './root/RootStore.interface'
88
// export the RootStore model
99
export * from './root/RootStore.model'
10-
// GEN-EXPORTS
1110
// as you add more state modules, add additional exports for those here as well
11+
// GEN-EXPORTS
1212
export * from './items/ItemsState.interface'
1313
export * from './locales/LocalesState.interface'

Diff for: composition-api/src/models/store/module-names/StoreModuleNames.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// group our constants in a namespace:
22
export namespace StoreModuleNames {
3+
// GEN-NAMESPACE-CONSTS
34
export const itemsState: string = 'itemsState'
45
export const localesState: string = 'localesState'
56
// as you add more state modules, add additional properties here following the same convention

Diff for: composition-api/src/models/store/mutation-type/MutationType.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// group our constants in a namespace
2+
// GEN-IMPORTS
23
import { ItemsMutationType } from '../items/ItemsMutationType'
34
import { LocalesMutationType } from '../locales/LocalesMutationType'
45

56
export namespace MutationType {
7+
// GEN-NAMESPACE-CONSTS
68
export const items = ItemsMutationType
79
export const locales = LocalesMutationType
810
// as you add more domain-specific mutation types, add them here following the same convention

Diff for: composition-api/src/models/store/root/RootStore.interface.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
1-
import {
2-
// GEN-IMPORTS
3-
ItemsStateInterface,
4-
LocalesStateInterface
5-
} from '@/models/store'
6-
7-
//import { RootStateInterface } from './RootState.interface'
1+
// GEN-IMPORTS
2+
import { ItemsStateInterface } from '../items/ItemsState.interface'
3+
import { LocalesStateInterface } from '../locales/LocalesState.interface'
84

95
/**
106
* @name RootStoreInterface
117
* @description
128
* Wraps together each store module interface in one place
139
*/
1410
export interface RootStoreInterface {
15-
//extends RootStateInterface {
1611
// GEN-INTERFACE-PROPS
1712
itemsState: ItemsStateInterface
1813
localesState: LocalesStateInterface

Diff for: composition-api/src/store/items/index.ts

+20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { initialItemsState } from './initialState'
77
import { ItemInterface } from '@/models/items/Item.interface'
88
import apiClient from '@/api-client'
99

10+
/**
11+
* @name mutations
12+
* @description
13+
* Vuex Items mutations
14+
*/
1015
export const mutations: MutationTree<ItemsStateInterface> = {
1116
loadingItems(state: ItemsStateInterface) {
1217
state.loading = true
@@ -30,6 +35,11 @@ export const mutations: MutationTree<ItemsStateInterface> = {
3035
}
3136
}
3237

38+
/**
39+
* @name actions
40+
* @description
41+
* Vuex Items actions
42+
*/
3343
export const actions: ActionTree<ItemsStateInterface, RootStateInterface> = {
3444
loadItems({ commit }) {
3545
commit(MutationType.items.loadingItems)
@@ -54,12 +64,22 @@ export const actions: ActionTree<ItemsStateInterface, RootStateInterface> = {
5464
}
5565
}
5666

67+
/**
68+
* @name getters
69+
* @description
70+
* Vuex Items getters
71+
*/
5772
export const getters: GetterTree<ItemsStateInterface, RootStateInterface> = {}
5873

5974
// create our Items store instance
6075
const namespaced: boolean = true
6176
const state: ItemsStateInterface = initialItemsState
6277

78+
/**
79+
* @name itemsState
80+
* @description
81+
* Vuex Items store
82+
*/
6383
export const itemsState: Module<ItemsStateInterface, RootStateInterface> = {
6484
namespaced,
6585
state,

Diff for: composition-api/src/store/locales/index.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Module, MutationTree, ActionTree, GetterTree } from 'vuex'
2-
32
import { MutationType, RootStateInterface, LocalesStateInterface } from '@/models/store'
4-
53
import { initialLocalesState } from './initialState'
6-
7-
import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
84
import { i18n } from '@/plugins/vue-i18n-next-plugin'
95
import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'
106

7+
/**
8+
* @name mutations
9+
* @description
10+
* Vuex Locales mutations
11+
*/
1112
export const mutations: MutationTree<LocalesStateInterface> = {
1213
selectLocale(state: LocalesStateInterface, localeId: string) {
1314
// set only the model selected to true
@@ -23,6 +24,11 @@ export const mutations: MutationTree<LocalesStateInterface> = {
2324
}
2425
}
2526

27+
/**
28+
* @name actions
29+
* @description
30+
* Vuex Locales actions
31+
*/
2632
export const actions: ActionTree<LocalesStateInterface, RootStateInterface> = {
2733
selectLocale({ commit }, localeId: string) {
2834
commit(MutationType.locales.selectLocale, localeId)

Diff for: composition-api/src/store/locales/initialState.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { LocalesStateInterface } from '@/models/store'
22
import { LocaleInfoInterface } from '@/models/localization/LocaleInfo.interface'
3-
import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'
4-
import { i18n } from '@/plugins/vue-i18n-next-plugin'
53
import { config } from '@/config'
64

75
// drive locales with config

Diff for: composition-api/src/store/root/index.ts

+67-5
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@ import {
55
RootStoreInterface,
66
RootStoreModel,
77
StoreModuleNames,
8-
MutationType
8+
MutationType,
9+
// GEN-IMPORTS-STATE-INTERFACE
10+
ItemsStateInterface,
11+
LocalesStateInterface
912
} from '@/models/store'
1013

14+
1115
import { initialRootState } from './initialState'
1216

1317
import { LocalStorageKeys } from '@/models/local-storage/LocalStorageKeys'
1418

15-
// GEN-IMPORTS
1619
// import each Vuex module
20+
// GEN-IMPORTS-STATE
1721
import { itemsState } from '@/store/items'
1822
import { localesState } from '@/store/locales'
1923

20-
// Vuex store options to build our modularized namespaced store
24+
/**
25+
* @name storeOptions
26+
* @description
27+
* Vuex store options to build our modularized namespaced store
28+
*/
2129
const storeOptions: StoreOptions<RootStateInterface> = {
2230
state: initialRootState,
2331

@@ -32,14 +40,68 @@ const storeOptions: StoreOptions<RootStateInterface> = {
3240
// Vuex Root store instance
3341
export const store: RootStoreModel<RootStateInterface> = <any>createStore(storeOptions)
3442

43+
/**
44+
* @name dispatchModuleAction
45+
* @description
46+
* Private helper to dispatch an action to a Vuex module from one place
47+
* So we keep the string interpolation for `${moduleName}/${actionName}` in one place only
48+
* and be able to dispatch action with less code in a strongly-type way
49+
* @param moduleName
50+
* @param actionName
51+
* @param params
52+
*/
53+
function dispatchModuleAction<T>(moduleName: string, actionName: string, params?: T): void {
54+
store.dispatch(`${moduleName}/${actionName}`, params)
55+
}
56+
57+
// create individual module-store wrappers by app domain
58+
// where each wrapper returns the corresponding module state from the Vuex Store
59+
// and has an action<T> that allows us to dispatch the action with less
60+
// code and in astrongly-typed way
61+
/**
62+
* @name itemsStore
63+
* @description
64+
* The items store wrapper that returns the itemsState and exposes a generic action<T> method
65+
*/
66+
const itemsStore = {
67+
get state(): ItemsStateInterface {
68+
return store.state.itemsState
69+
},
70+
action<T>(actionName: string, params?: T): void {
71+
dispatchModuleAction(StoreModuleNames.itemsState, actionName, params)
72+
}
73+
}
74+
75+
/**
76+
* @name localesStore
77+
* @description
78+
* The locales store wrapper that retuns the localesState and exposes a generic action<T> method
79+
*/
80+
const localesStore = {
81+
get state(): LocalesStateInterface {
82+
return store.state.localesState
83+
},
84+
action<T>(actionName: string, params?: T): void {
85+
dispatchModuleAction(StoreModuleNames.localesState, actionName, params)
86+
}
87+
}
88+
89+
// export our wrappers using the composition API convention (i.e. useXYZ)
90+
export const useItemsStore = () => {
91+
return itemsStore
92+
}
93+
export const useLocalesStore = () => {
94+
return localesStore
95+
}
96+
3597
// for the current locale,
3698
// try using the last user's preferred locale
3799
// if available from localStorage
38100
const userPreferredLocaleId: string = localStorage.getItem(LocalStorageKeys.locale) || ''
39101
if (userPreferredLocaleId.length > 0) {
40102
// change locale selected
41-
store.dispatch(
42-
`${StoreModuleNames.localesState}/${MutationType.locales.selectLocale}`,
103+
localesStore.action(
104+
MutationType.locales.selectLocale,
43105
userPreferredLocaleId
44106
)
45107
}

Diff for: composition-api/src/views/Home.vue

+6-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<script lang="ts">
88
import { defineComponent, computed, onMounted } from 'vue'
9-
import { store } from '@/store'
9+
import { useItemsStore } from '@/store'
1010
import { MutationType, StoreModuleNames } from '@/models/store'
1111
import ItemsListComponent from '@/components/items/ItemsList.component.vue'
1212
import { ItemInterface } from '@/models/items/Item.interface'
@@ -17,19 +17,20 @@
1717
ItemsListComponent
1818
},
1919
setup() {
20+
const itemsStore = useItemsStore()
2021
const items = computed(() => {
21-
return store.state.itemsState.items
22+
return itemsStore.state.items
2223
})
2324
const loading = computed(() => {
24-
return store.state.itemsState.loading
25+
return itemsStore.state.loading
2526
})
2627
2728
onMounted(() => {
28-
store.dispatch(`${StoreModuleNames.itemsState}/${MutationType.items.loadItems}`)
29+
itemsStore.action(MutationType.items.loadItems)
2930
})
3031
3132
const onSelectItem = (item: ItemInterface) => {
32-
store.dispatch(`${StoreModuleNames.itemsState}/${MutationType.items.selectItem}`, {
33+
itemsStore.action(MutationType.items.selectItem, {
3334
id: item.id,
3435
selected: !item.selected
3536
})

0 commit comments

Comments
 (0)