A look at state management on the web.
Including the like of Flux, Redux and Vuex.
The state is all of the information that is retain by your application, often with respect to previous events or interactions.
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.
State can be a messy part of application development, especially when there are a lot of user interactions to manage.
Flux is an architecture for creating data layers in JavaScript applications. It was designed at Facebook along with the React view library.
The dispatcher receives actions and dispatches them to stores that have registered with the dispatcher.
A store holds the data of an application.
Stores will register with the application's dispatcher so that they can receive actions.
Actions define the internal API of your application.
They capture the ways in which anything might interact with your application.
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.
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
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
}
]
}
*/
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'
})
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)
It is a self-contained app
export default {
computed: {
user() {
return this.$store.state.user;
}
}
}
import {mapState} from 'vuex';
export default {
computed: mapState({
user: state => state.user
})
}
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']),
}
}
export default new Vuex.Store({
...,
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done);
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length;
},
}
});