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
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
- Keystone
- Keystone Component
State Management
By Thomas Truong
State Management
State Management Slides
- 77