State Management and Vuex

A look at state management on the web.

Including the like of Flux, Redux and Vuex.

Agenda

  • What is State Management?
  • Flux
  • Redux
  • Vuex
  • Fun Exercises

What is State?

The state is all of the information that is retain by your application, often with respect to previous events or interactions.

What is State Management?

State management makes the state of your application tangible in the form of a data structure that you can read from and write to. It makes your invisible state clearly visible for you to work with.

Why does it matter?

State can be a messy part of application development, especially when there are a lot of user interactions to manage.

What is Flux?

Flux is an architecture for creating data layers in JavaScript applications. It was designed at Facebook along with the React view library.

 

  • Explicit and understandable update paths
  • Tracing changes during development simpler
  • Bugs easier to track down and fix

Flux Components

  • Dispatcher
  • Store
  • Action
  • View

Dispatcher

The dispatcher receives actions and dispatches them to stores that have registered with the dispatcher.

 

Stores

A store holds the data of an application.

 

Stores will register with the application's dispatcher so that they can receive actions.

 

Actions

Actions define the internal API of your application.

 

They capture the ways in which anything might interact with your application.

Views

Data from stores are displayed in views.

 

When a view uses data from a store it must also subscribe to change events from that store.

Redux

  • Predictable state container for JavaScript apps
  • Helps you write applications that:
    • ​Behave consistently
    • Run in different environments (client, server, and native)
    • Are easy to test.

Redux vs Flux

  • Like Flux, Redux prescribes that you concentrate your model update logic in a certain layer of your application (“stores” in Flux, “reducers” in Redux)

 

  • Unlike Flux, Redux does not have the concept of a Dispatcher

 

  • Another important difference from Flux is that Redux assumes you never mutate your data

Typical App using Redux

import { createStore } from 'redux'

/**
 * This is a reducer, a pure function with (state, action) => state signature.
 * It describes how an action transforms the state into the next state.
 *
 * The shape of the state is up to you: it can be a primitive, an array, an object,
 * or even an Immutable.js data structure. The only important part is that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * In this example, we use a `switch` statement and strings, but you can use a helper that
 * follows a different convention (such as function maps) if it makes sense for your
 * project.
 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counter)

// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// However it can also be handy to persist the current state in the localStorage.

store.subscribe(() => console.log(store.getState()))

// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

Redux Core Concepts

  • specify the mutations you want to happen with plain objects called actions
  • use special function called a reducer to decide how every action transforms the entire application's state
  • a single store with a single root reducing function

Single source of truth

The state of your whole application is stored in an object tree within a single store.

console.log(store.getState())

/* Prints
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
*/

State is read-only

The only way to change the state is to emit an action, an object describing what happened.

store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})

store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
})

Changes are made with pure functions

To specify how the state tree is transformed by actions, you write pure reducers.

function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)

Vuex

  • A state management pattern and library for Vue.js applications
  • It serves as a centralised store for all the components in an application
  • It can only be mutated in a predictable fashion

One-way data flow

What is a State Manage Pattern?

It is a self-contained app

  • The state, the source of truth 
  • The view, a declarative mapping of the state
  • The actions, the possible ways the state could change 

 

What is State in your App?

Without Vuex

Changing State

Default Communication

Single Source of Truth

State is Reactive

Need Standardisation

Vuex Store

X-Men

Getters

Vuex in Motion

Lab

  • Please click on fork for your own version
  • Remember to save for the changes to take effect

mapState

export default {
  computed: {
    user() {
      return this.$store.state.user;
    }
  }
}
import {mapState} from 'vuex';

export default {
  computed: mapState({
    user: state => state.user
  })
}

mapState shortcuts

import {mapState} from 'vuex';

export default {
  computed: mapState({
    user: state => state.user
  })
}
import {mapState} from 'vuex';

export default {
  computed: mapState({ user: 'user' })
}
import {mapState} from 'vuex';

export default {
  computed: mapState(['user'])
}
import {mapState} from 'vuex';

export default {
  computed: {
    localComputed() { return 'something'; },
    ...mapState(['user']),
  }
}

Lab 1

  • Create title in your store with 'My Hero Academia'
  • By using mapState update the title from the store
  • Using a computed property return your name

Lab 2

  • Move heroes list from data to state

Getters

export default new Vuex.Store({
  ...,
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done);
    },
    doneTodosCount: (state, getters) => {
      return getters.doneTodos.length;
    },
  }
});

Lab 3

  • create a getter call currentHeroes which filters out 'Iron Man'
  • create a getter call countHeroes which counts currentHeroes
  • update OnCall.vue accordingly to consumer getters

Lab 4

  • create an ADD_HERO mutation which adds a new hero in the state
  • map the mutation onto Hero.vue
  • create the method addHero to execute mapped mutation with the input newHero

Lab 5

  • create a REMOVE_HERO mutation which removes a hero in the state
  • create an action removeHero to commit the mutation
  • map the actions onto Hero.vue
  • create the method removeHeroes on the component to execute the mapped action with the corresponding index

Lab 6

  • create a REMOVE_ALL mutation which makes heroes in state as empty
  • create an action removeAll to commit the mutation
  • map the actions onto OnCall.vue
  • create the method removeAllHeroes on the component to execute the mapped action and set msg to 'All heroes have been removed'

Resources

Resources

Made with Slides.com