diff options
Diffstat (limited to 'doc/development/fe_guide/vuex.md')
-rw-r--r-- | doc/development/fe_guide/vuex.md | 60 |
1 files changed, 32 insertions, 28 deletions
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md index 7765ba04d40..1d83335291a 100644 --- a/doc/development/fe_guide/vuex.md +++ b/doc/development/fe_guide/vuex.md @@ -1,19 +1,19 @@ --- stage: none group: unassigned -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- # Vuex -When there's a clear benefit to separating state management from components (e.g. due to state complexity) we recommend using [Vuex](https://vuex.vuejs.org) over any other Flux pattern. Otherwise, feel free to manage state within the components. +When there's a clear benefit to separating state management from components (for example, due to state complexity) we recommend using [Vuex](https://vuex.vuejs.org) over any other Flux pattern. Otherwise, feel free to manage state in the components. Vuex should be strongly considered when: -- You expect multiple parts of the application to react to state changes -- There's a need to share data between multiple components -- There are complex interactions with Backend, e.g. multiple API calls -- The app involves interacting with backend via both traditional REST API and GraphQL (especially when moving the REST API over to GraphQL is a pending backend task) +- You expect multiple parts of the application to react to state changes. +- There's a need to share data between multiple components. +- There are complex interactions with Backend, for example, multiple API calls. +- The app involves interacting with backend via both traditional REST API and GraphQL (especially when moving the REST API over to GraphQL is a pending backend task). The information included in this page is explained in more detail in the official [Vuex documentation](https://vuex.vuejs.org). @@ -22,7 +22,7 @@ official [Vuex documentation](https://vuex.vuejs.org). 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. The action itself will not update the state; only a mutation should update the state. +When a user clicks on an action, we need to `dispatch` it. This action `commits` a mutation that changes the state. The action itself does not update the state; only a mutation should update the state. ## File structure @@ -92,7 +92,7 @@ An action is a payload of information to send data from our application to our s An action is usually composed by a `type` and a `payload` and they describe what happened. Unlike [mutations](#mutationsjs), actions can contain asynchronous operations - that's why we always need to handle asynchronous logic in actions. -In this file, we will write the actions that will call mutations for handling a list of users: +In this file, we write the actions that call mutations for handling a list of users: ```javascript import * as types from './mutation_types'; @@ -163,15 +163,15 @@ Instead of creating an mutation to toggle the loading state, we should: - `PUT`: `updateSomething` - `DELETE`: `deleteSomething` -As a result, we can dispatch the `fetchNamespace` action from the component and it will be responsible to commit `REQUEST_NAMESPACE`, `RECEIVE_NAMESPACE_SUCCESS` and `RECEIVE_NAMESPACE_ERROR` mutations. +As a result, we can dispatch the `fetchNamespace` action from the component and it is responsible to commit `REQUEST_NAMESPACE`, `RECEIVE_NAMESPACE_SUCCESS` and `RECEIVE_NAMESPACE_ERROR` mutations. -> Previously, we were dispatching actions from the `fetchNamespace` action instead of committing mutation, so please don't be confused if you find a different pattern in the older parts of the codebase. However, we encourage leveraging a new pattern whenever you write new Vuex stores +> Previously, we were dispatching actions from the `fetchNamespace` action instead of committing mutation, so please don't be confused if you find a different pattern in the older parts of the codebase. However, we encourage leveraging a new pattern whenever you write new Vuex stores. By following this pattern we guarantee: -1. All applications follow the same pattern, making it easier for anyone to maintain the code -1. All data in the application follows the same lifecycle pattern -1. Unit tests are easier +1. All applications follow the same pattern, making it easier for anyone to maintain the code. +1. All data in the application follows the same lifecycle pattern. +1. Unit tests are easier. #### Updating complex state @@ -215,7 +215,7 @@ While this approach works it has several dependencies: - Correct selection of `item` in the component/action. - The `item` property is already declared in the `closed` state. - A new `confidential` property would not be reactive. -- Noting that `item` is referenced by `items` +- Noting that `item` is referenced by `items`. A mutation written like this is harder to maintain and more error prone. We should rather write a mutation like this: @@ -245,7 +245,7 @@ A mutation written like this is easier to maintain. In addition, we avoid errors ### `getters.js` Sometimes we may need to get derived state based on store state, like filtering for a specific prop. -Using a getter will also cache the result based on dependencies due to [how computed props work](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods) +Using a getter also caches the result based on dependencies due to [how computed props work](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods) This can be done through the `getters`: ```javascript @@ -272,7 +272,10 @@ import { mapGetters } from 'vuex'; ### `mutation_types.js` From [vuex mutations docs](https://vuex.vuejs.org/guide/mutations.html): -> It is a commonly seen pattern to use constants for mutation types in various Flux implementations. This allows the code to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application. +> It is a commonly seen pattern to use constants for mutation types in various Flux implementations. +> This allows the code to take advantage of tooling like linters, and putting all constants in a +> single file allows your collaborators to get an at-a-glance view of what mutations are possible +> in the entire application. ```javascript export const ADD_USER = 'ADD_USER'; @@ -346,7 +349,7 @@ export default ({ #### Why not just ...spread the initial state? -The astute reader will see an opportunity to cut out a few lines of code from +The astute reader sees an opportunity to cut out a few lines of code from the example above: ```javascript @@ -359,17 +362,17 @@ export default initialState => ({ }); ``` -We've made the conscious decision to avoid this pattern to aid in the -discoverability and searchability of our frontend codebase. The same applies +We made the conscious decision to avoid this pattern to improve the ability to +discover and search our frontend codebase. The same applies when [providing data to a Vue app](vue.md#providing-data-from-haml-to-javascript). The reasoning for this is described in [this discussion](https://gitlab.com/gitlab-org/frontend/rfcs/-/issues/56#note_302514865): > Consider a `someStateKey` is being used in the store state. You _may_ not be > able to grep for it directly if it was provided only by `el.dataset`. Instead, -> you'd have to grep for `some_state_key`, since it could have come from a rails +> you'd have to grep for `some_state_key`, because it could have come from a Rails > template. The reverse is also true: if you're looking at a rails template, you > might wonder what uses `some_state_key`, but you'd _have_ to grep for -> `someStateKey` +> `someStateKey`. ### Communicating with the Store @@ -426,12 +429,12 @@ export default { #### Testing Vuex concerns -Refer to [vuex docs](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations. +Refer to [Vuex docs](https://vuex.vuejs.org/guide/testing.html) regarding testing Actions, Getters and Mutations. #### Testing components that need a store -Smaller components might use `store` properties to access the data. -In order to write unit tests for those components, we need to include the store and provide the correct state: +Smaller components might use `store` properties to access the data. To write unit tests for those +components, we need to include the store and provide the correct state: ```javascript //component_spec.js @@ -482,8 +485,9 @@ describe('component', () => { ### Two way data binding -When storing form data in Vuex, it is sometimes necessary to update the value stored. The store should never be mutated directly, and an action should be used instead. -In order to still use `v-model` in our code, we need to create computed properties in this form: +When storing form data in Vuex, it is sometimes necessary to update the value stored. The store +should never be mutated directly, and an action should be used instead. +To use `v-model` in our code, we need to create computed properties in this form: ```javascript export default { @@ -521,7 +525,7 @@ export default { }; ``` -Adding a few of these properties becomes cumbersome, and makes the code more repetitive with more tests to write. To simplify this there is a helper in `~/vuex_shared/bindings.js` +Adding a few of these properties becomes cumbersome, and makes the code more repetitive with more tests to write. To simplify this there is a helper in `~/vuex_shared/bindings.js`. The helper can be used like so: @@ -568,4 +572,4 @@ export default { } ``` -`mapComputed` will then generate the appropriate computed properties that get the data from the store and dispatch the correct action when updated. +`mapComputed` then generates the appropriate computed properties that get the data from the store and dispatch the correct action when updated. |