Local state is fine
Redux [adds] indirection to decouple “what happened” from “how things change”
- Dan Abramov, You Might Not Need Redux
When [you] have multiple components that share common state
import React, { Component } from 'react';
class Counter extends Component {
state = { value: 0 };
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
decrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
import React, { Component } from 'react';
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
)
}
}
import React, { Component } from 'react';
const counter = (state = { value: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { value: state.value + 1 };
case 'DECREMENT':
return { value: state.value - 1 };
default:
return state;
}
}
const StatelessCounter = ({ value, increment, decrement }) => {
return (
<div>
{value}
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
class Counter extends Component {
state = counter(undefined, {});
dispatch(action) {
this.setState(prevState => counter(prevState, action));
}
increment = () => {
this.dispatch({ type: 'INCREMENT' });
};
decrement = () => {
this.dispatch({ type: 'DECREMENT' });
};
render() {
return (
<StatelessCounter value={this.state.value} increment={this.increment} decrement={decrement} />
);
}
}
Redux is a predictable state container for JavaScript apps.
Describe application state as plain objects
and arrays, single source of truth
You can use an initial state object to describe the entire application
Describe changes in the system as plain
objects, state is read-only
Actions are dispatched from component, or other actions
Describe the logic for handling changes as
pure functions
State is updated using reducers, which accept an accumulation and a value and returns a new accumulation
{
transient: { // does not survive a refresh
selectedElement: 'element-d88c-4bbd-9453-db22e949b92e',
selectedPage: 'page-f3ce-4bb7-86c8-0417606d6592',
resolvedArgs: {
'element-d88c-4bbd-9453-db22e949b92e': {
expressionContexts: {
state: 'ready',
value: {
type: 'datatable',
columns: ['id', 'username', 'time', 'cost', 'price'],
},
error: null,
},
},
},
},
persistent: { // survives refresh, be serialized and be saved
workpad: {
name: 'Untitled Workpad',
id: 'workpad-2235-4af1-b781-24e42a453e5b',
pages: [{
id: 'page-f3ce-4bb7-86c8-0417606d6592',
elements: [{
id: 'element-d88c-4bbd-9453-db22e949b92e',
expression: 'demodata().sort("time").pointseries(x="time", y=.math("sum(price)")).line()',
ast: '...',
}],
}],
},
},
}
import { ADD_TODO } from '../actionTypes'
// action creator
export addTodo = (todo) => ({
type: ADD_TODO,
payload: todo,
})
import { ADD_TODO } from '../actionTypes'
export default function todoReducer((state, action) => {
switch(action.type) {
case ADD_TODO:
// handle the request action
return Object.assign({}, state, {
todos: state.todos.concat(action.payload),
});
default:
return state;
}
});
import fetch from 'axios';
import { ADD_TODO_SUCCESS, ADD_TODO_ERROR, ADD_TODO_REQUEST } from '../actionTypes';
// action creator
export addTodo = (todo) => (dispatch, getState) => {
fetch.get('...')
.then((data) => {
dispatch({
type: ADD_TODO_SUCCESS,
payload: data,
});
})
.catch((err) => {
dispatch({
type: ADD_TODO_ERROR,
error: err,
payload: null,
});
});
return {
type: ADD_TODO_REQUEST,
payload: null,
};
}
};
import thunk from 'redux-thunk';
import combineReducers from 'redux';
import initialState from './initial_state';
import * as reducers from './reducers';
const rootReducer = combineReducers({
app: reducers.appReducer,
transient: combineReducers({
properties: reducers.transientReducer,
resolvedArgs: reducers.resolvedArgsReducer,
}),
persistent: combineReducers({
workpad: combineReducers({
properties: reducers.workpadReducer,
pages: combineReducers({
properties: reducers.pagesReducer,
elements: reducers.elementsReducer,
})
})
}),
});
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
Vuex is a state management pattern and library for Vue.js applications
Describe application state using a global
singleton that enforces rules for changes
You can use an initial state object to describe the entire application
Changes in the system happen by dispatching
actions and committing mutations
State is mutated in the global singleton, using predictable rules
A library implementation that takes advantage of Vue's granular reactivity system
Mutations are done on observable objects that trigger updates in components
import Vuex from 'vuex';
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
decrement(state) {
state.count--;
},
},
});
import Vuex from 'vuex';
import fetch from 'axios';
const store = new Vuex.Store({
state: {
todos: [],
},
mutations: {
pushTodo(state, todo) {
state.todos.push(todo);
},
},
actions: {
addTodo({ commit }) {
return fetch.get('...')
.then(data => commit('pushTodo', data));
},
},
});
import Vuex from 'vuex';
import fetch from 'axios';
const store = new Vuex.Store({
state: {
todos: [],
},
mutations: {
pushTodo(state, todo){
state.todos.push(todo);
},
},
actions: {
addTodo({ commit, dispatch }) {
return fetch.get('...')
.then(data => {
dispatch('anotherAction');
commit('pushTodo', data)
});
},
},
});
import Vue from 'vue';
var todoApp = new Vue({
el: '#todoApp',
data: {
working: false,
}
computed: {
todos() {
return this.$store.todos;
},
},
methods: {
addTodo: function (todo) {
this.working = true;
this.$store.dispatch('addTodo', todo)
.then(() => { this.working = false; });
},
},
});
If I was going to sum up my experiences with Vue in a sentence, I'd probably say something like "it's just so reasonable"
- Sarah Drasner, Intro to Vue.js
vue-router and vuex
Next Month!
June 28th