Skip to content

Migrate: State management #94

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ const sidebar = {
{
title: 'Scaling Up',
collapsable: false,
children: ['/guide/routing']
children: [
'/guide/routing',
'/guide/state-management'
]
},
{
title: 'Migration to Vue 3',
Expand Down
Binary file added src/.vuepress/public/images/state.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/guide/accessibility.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Accessiblity

An accessible website is a website that can be used by anyone — be that a person with a disability, someone on a slow connection, or someone whose hardware is dated or broken. It's easy to make a website assuming that all our users are using a keyboard, mouse, and screen, and have a way of hearing the sound produced by our websites, but that often isn't true: millions of people around the world have disabilities and are unable to use all the functionality of a computer in the same way most developers do.

While many people with permanent disabilities might have tooling to help them, they're also relying on the people building the websites to make them accessible and work well with the tooling.
Expand Down
98 changes: 98 additions & 0 deletions src/guide/state-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# State Management

## Official Flux-Like Implementation

Large applications can often grow in complexity, due to multiple pieces of state scattered across many components and the interactions between them. To solve this problem, Vue offers [vuex](https://github.com/vuejs/vuex), our own Elm-inspired state management library. It even integrates into [vue-devtools](https://github.com/vuejs/vue-devtools), providing zero-setup access to [time travel debugging](https://raw.githubusercontent.com/vuejs/vue-devtools/master/media/demo.gif).

<!--TODO: use an updated tutorial for 3.0?-->
<div class="vue-mastery"><a href="https://www.vuemastery.com/courses/mastering-vuex/intro-to-vuex/" target="_blank" rel="sponsored noopener" title="Vuex Tutorial">Watch a video explanation on Vue Mastery</a></div>

### Information for React Developers

If you're coming from React, you may be wondering how vuex compares to [redux](https://github.com/reactjs/redux), the most popular Flux implementation in that ecosystem. Redux is actually view-layer agnostic, so it can easily be used with Vue via [simple bindings](https://classic.yarnpkg.com/en/packages?q=redux%20vue&p=1). Vuex is different in that it _knows_ it's in a Vue app. This allows it to better integrate with Vue, offering a more intuitive API and improved development experience.

## Simple State Management from Scratch

It is often overlooked that the source of truth in Vue applications is the raw `data` object - a Vue instance only proxies access to it. Therefore, if you have a piece of state that should be shared by multiple instances, you can share it by identity:

``` js
const sourceOfTruth = {
message: 'Hello'
}

const appA = Vue.createApp({
data () {
return sourceOfTruth
}
}).mount('#app-a')

const appB = Vue.createApp({
data () {
return sourceOfTruth
}
}).mount('#app-b')
```

Now whenever `sourceOfTruth` is mutated, both `appA` and `appB` will update their views automatically. Subcomponents within each of these instances would also have access via `this.$root.$data`. We have a single source of truth now, but debugging would be a nightmare. Any piece of data could be changed by any part of our app at any time, without leaving a trace.

To help solve this problem, we can adopt a **store pattern**:

``` js
const store = {
debug: true,

state: {
message: 'Hello!'
},

setMessageAction (newValue) {
if (this.debug) {
console.log('setMessageAction triggered with', newValue)
}

this.state.message = newValue
},

clearMessageAction () {
if (this.debug) {
console.log('clearMessageAction triggered')
}

this.state.message = ''
}
}
```

Notice all actions that mutate the store's state are put inside the store itself. This type of centralized state management makes it easier to understand what type of mutations could happen and how they are triggered. Now when something goes wrong, we'll also have a log of what happened leading up to the bug.

In addition, each instance/component can still own and manage its own private state:

``` js
const appA = Vue.createApp({
data () {
return {
privateState: {},
sharedState: store.state
}
}
}).mount('#app-a')

const appB = Vue.createApp({
data () {
return {
privateState: {},
sharedState: store.state
}
}
}).mount('#app-b')
```

![State Management](/images/state.png)

::: tip
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
::: tip
::: danger

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original type for this message on vuejs.org is "success." I don't think it's a "dangerous" action per-se, more like a tip (hence "Tip"). TBH I'm tempted to blend this "tip" into the content altogether and avoid the flow breakage. WDYT?

You should never replace the original state object in your actions - the components and the store need to share reference to the same object in order for mutations to be observed.
:::

As we continue developing the convention where components are never allowed to directly mutate state that belongs to a store, but should instead dispatch events that notify the store to perform actions, we eventually arrive at the [Flux](https://facebook.github.io/flux/) architecture. The benefit of this convention is we can record all state mutations happening to the store and implement advanced debugging helpers such as mutation logs, snapshots, and history re-rolls / time travel.

This brings us full circle back to [vuex](https://github.com/vuejs/vuex), so if you've read this far it's probably time to try it out!