Fixes typos and adds mutations examples
This commit is contained in:
parent
fef1e9d47b
commit
dd3b1526af
|
@ -73,6 +73,11 @@ Vue specific design patterns and practices.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [Vuex](vuex.md)
|
||||||
|
Vuex specific design patterns and practices.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [Axios](axios.md)
|
## [Axios](axios.md)
|
||||||
Axios specific practices and gotchas.
|
Axios specific practices and gotchas.
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,16 @@
|
||||||
|
# Vuex
|
||||||
## Vuex
|
|
||||||
To manage the state of an application you may use [Vuex][vuex-docs].
|
To manage the state of an application you may use [Vuex][vuex-docs].
|
||||||
|
|
||||||
_Note:_ All of the below is explained in more detail in the official [Vuex documentation][vuex-docs].
|
_Note:_ All of the below is explained in more detail in the official [Vuex documentation][vuex-docs].
|
||||||
|
|
||||||
### Separation of concerns
|
## Separation of concerns
|
||||||
Vuex is composed of State, Getters, Mutations, Actions and Modules.
|
Vuex is composed of State, Getters, Mutations, Actions and Modules.
|
||||||
|
|
||||||
When a user clicks on an action, we need to `dispatch` it. This action will `commit` a mutation that will change the state.
|
When a user clicks on an action, we need to `dispatch` it. This action will `commit` a mutation that will change the state.
|
||||||
_Note:_ The action itself will not update the state, only a mutation should update the state.
|
_Note:_ The action itself will not update the state, only a mutation should update the state.
|
||||||
|
|
||||||
#### File structure
|
## File structure
|
||||||
When using Vuex at GitLab, separate this concerns into different files to improve readability. If you can, separate the Mutation Types as well:
|
When using Vuex at GitLab, separate this concerns into different files to improve readability:
|
||||||
|
|
||||||
```
|
```
|
||||||
└── store
|
└── store
|
||||||
|
@ -19,11 +18,12 @@ When using Vuex at GitLab, separate this concerns into different files to improv
|
||||||
├── actions.js # actions
|
├── actions.js # actions
|
||||||
├── mutations.js # mutations
|
├── mutations.js # mutations
|
||||||
├── getters.js # getters
|
├── getters.js # getters
|
||||||
|
├── state.js # getters
|
||||||
└── mutation_types.js # mutation types
|
└── mutation_types.js # mutation types
|
||||||
```
|
```
|
||||||
The following examples show an application that lists and adds users to the state.
|
The following example shows an application that lists and adds users to the state.
|
||||||
|
|
||||||
##### `index.js`
|
### `index.js`
|
||||||
This is the entry point for our store. You can use the following as a guide:
|
This is the entry point for our store. You can use the following as a guide:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
|
@ -32,6 +32,7 @@ import Vuex from 'vuex';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
import * as getters from './getters';
|
import * as getters from './getters';
|
||||||
import mutations from './mutations';
|
import mutations from './mutations';
|
||||||
|
import state from './state';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
@ -39,19 +40,38 @@ export default new Vuex.Store({
|
||||||
actions,
|
actions,
|
||||||
getters,
|
getters,
|
||||||
mutations,
|
mutations,
|
||||||
state: {
|
state,
|
||||||
users: [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
_Note:_ If the state of the application is too complex, an individual file for the state may be better.
|
|
||||||
|
|
||||||
#### `actions.js`
|
### `state.js`
|
||||||
|
The first thing you should do before writing any code is to design the state.
|
||||||
|
|
||||||
|
Often we need to provide data from haml to our Vue application. Let's store it in the state for better access.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export default {
|
||||||
|
endpoint: null,
|
||||||
|
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
|
||||||
|
isAddingUser: false,
|
||||||
|
errorAddingUser: false,
|
||||||
|
|
||||||
|
users: [],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Access `state` properties
|
||||||
|
You can use `mapState` to access state properties in the components.
|
||||||
|
|
||||||
|
### `actions.js`
|
||||||
An action is a playload of information to send data from our application to our store.
|
An action is a playload of information to send data from our application to our store.
|
||||||
They are the only source of information for the store.
|
They are the only source of information for the store.
|
||||||
|
|
||||||
An action is usually composed by a `type` and a `payload` and they describe what happened.
|
An action is usually composed by a `type` and a `payload` and they describe what happened.
|
||||||
By enforcing that every change is described as an action lets us have a clear understantid of what is going on in the app.
|
Enforcing that every change is described as an action lets us have a clear understanting of what is going on in the app.
|
||||||
|
|
||||||
An action represents something that will trigger a state change, for example, when the user enters the page we need to load resources.
|
An action represents something that will trigger a state change, for example, when the user enters the page we need to load resources.
|
||||||
|
|
||||||
|
@ -84,10 +104,9 @@ In this file, we will write the actions (both sync and async) that will call the
|
||||||
.then(({ data }) => dispatch('receiveAddUserSuccess', data))
|
.then(({ data }) => dispatch('receiveAddUserSuccess', data))
|
||||||
.catch((error) => dispatch('receiveAddUserError', error));
|
.catch((error) => dispatch('receiveAddUserError', error));
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Actions Pattern: `request` and `receive` namespaces
|
#### Actions Pattern: `request` and `receive` namespaces
|
||||||
When a request is made we often want to show a loading state to the user.
|
When a request is made we often want to show a loading state to the user.
|
||||||
|
|
||||||
Instead of creating an action to toggle the loading state and dispatch it in the component,
|
Instead of creating an action to toggle the loading state and dispatch it in the component,
|
||||||
|
@ -107,7 +126,7 @@ By following this patter we guarantee:
|
||||||
1. Unit tests are easier
|
1. Unit tests are easier
|
||||||
1. Actions are simple and straightforward
|
1. Actions are simple and straightforward
|
||||||
|
|
||||||
##### Dispatching actions
|
#### Dispatching actions
|
||||||
To dispatch an action from a component, use the `mapActions` helper:
|
To dispatch an action from a component, use the `mapActions` helper:
|
||||||
```javascript
|
```javascript
|
||||||
import { mapActions } from 'vuex';
|
import { mapActions } from 'vuex';
|
||||||
|
@ -124,6 +143,9 @@ import { mapActions } from 'vuex';
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Handling errors with `createFlash`
|
||||||
|
// TODO
|
||||||
|
|
||||||
#### `mutations.js`
|
#### `mutations.js`
|
||||||
The mutations specify how the application state changes in response to actions sent to the store.
|
The mutations specify how the application state changes in response to actions sent to the store.
|
||||||
The only way to actually change state in a Vuex store is by committing a mutation.
|
The only way to actually change state in a Vuex store is by committing a mutation.
|
||||||
|
@ -138,14 +160,29 @@ Remember that actions only describe the fact that something happened, they don't
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
[types.ADD_USER](state, user) {
|
[types.REQUEST_USERS](state) {
|
||||||
|
Object.assign(state, { isLoading: true });
|
||||||
|
},
|
||||||
|
[types.RECEIVE_USERS_SUCCESS](state, data) {
|
||||||
|
// Do any needed data transformation to the received payload here
|
||||||
|
Object.assign(state, { users: data, isLoading: false });
|
||||||
|
},
|
||||||
|
[types.REQUEST_USERS_ERROR](state, error) {
|
||||||
|
Object.assign(state, { isLoading: false, error});
|
||||||
|
},
|
||||||
|
[types.REQUEST_ADD_USER](state, user) {
|
||||||
|
Object.assign(state, { isAddingUser: true });
|
||||||
|
},
|
||||||
|
[types.RECEIVE_ADD_USER_SUCCESS](state, user) {
|
||||||
|
Object.assign(state, { isAddingUser: false });
|
||||||
state.users.push(user);
|
state.users.push(user);
|
||||||
},
|
},
|
||||||
|
[types.REQUEST_ADD_USER_ERROR](state, error) {
|
||||||
|
Object.assign(state, { isAddingUser: true , errorAddingUser: error});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### `getters.js`
|
#### `getters.js`
|
||||||
Sometimes we may need to get derived state based on store state, like filtering for a specific prop.
|
Sometimes we may need to get derived state based on store state, like filtering for a specific prop.
|
||||||
This can be done through the `getters`:
|
This can be done through the `getters`:
|
||||||
|
@ -191,6 +228,62 @@ The store should be included in the main component of your application:
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Communicating with the Store
|
||||||
|
```javascript
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
store,
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'getUsersWithPets'
|
||||||
|
]),
|
||||||
|
...mapState([
|
||||||
|
'isLoading',
|
||||||
|
'users',
|
||||||
|
'error',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions([
|
||||||
|
'fetchUsers',
|
||||||
|
'addUser',
|
||||||
|
]),
|
||||||
|
|
||||||
|
onClickAddUser(data) {
|
||||||
|
this.addUser(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.fetchUsers()
|
||||||
|
.catch(() => {
|
||||||
|
// TODO - Decide where to handle the `createFlash`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<ul>
|
||||||
|
<li v-if="isLoading">
|
||||||
|
Loading...
|
||||||
|
</li>
|
||||||
|
<li v-else-if="error">
|
||||||
|
{{ error }}
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-else
|
||||||
|
v-for="user in users"
|
||||||
|
:key="user.id"
|
||||||
|
>
|
||||||
|
{{ user }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
### Vuex Gotchas
|
### Vuex Gotchas
|
||||||
1. Do not call a mutation directly. Always use an action to commit a mutation. Doing so will keep consistency through out the application. From Vuex docs:
|
1. Do not call a mutation directly. Always use an action to commit a mutation. Doing so will keep consistency through out the application. From Vuex docs:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue