"Because You're Not That Good At State Management"
Christopher Bloom, @illepic
slides.com/illepic/redux
Image via /u/dataMinery
(function($) {
if ($(".header-desktop .dropdown.active-trail").length > 0) {
$('body').addClass('header-desktop-subnav--is-active');
}
})(jQuery);
header_type: desktop/mobile
active_trail: true/false
subnav_active: true/false
Image via Justin Maller
Just a view layer
Batteries included
React: Not a full framework
Vue: Not a full framework
React + Redux: View + Data flow
Vue + VueX: View + Data flow
Angular 4: Getting closer
Angular 1: Kitchen sink framework
Ember: All the things
Flux is the application architecture that Facebook uses for building client-side web applications. It complements React's composable view components by utilizing a unidirectional data flow. It's more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.
Growth of GitHub stars over time for Flux libraries (Redux in green)
All data follows the same lifecycle pattern. Apps are predictable and easier to understand.
Image via Michael Muraz
Action
Reducer
Store
View
API
User clicks button, ajax!
Response returned, an ACTION is DISPATCHED.
{
type: ‘RESPONSE_SUCCESS’,
payload: response.data
}
STORE sees dispatched ACTION, runs REDUCER
REDUCER function updates STORE based on ACTION type
After state in STORE is updated, components registered to VIEW update UI
Actions are payloads of information that send data from your application to your store. They are the only source of information for the store.
{
type: 'INCREMENT',
value: 1,
}
They are simple objects with a type and a payload.
That's it. Every single thing your application can do will be documented in ACTIONS.
Actions only describe that something happened, but don't describe how the app state changes in response. This is the job of a REDUCER.
The REDUCER is a pure function that takes the previous state and an action, and returns the next state.
function counter(state, action) {
if (typeof state === 'undefined') {
return 0
}
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
var store = Redux.createStore(counter);
Image via felixufpe
"Won't my REDUCER switch statements get huge with a nontrivial app?"
Yes!
combineReducers() let's you disperse reducers throughout the project and combine them all together easily.
// reducers/todos.js
export default function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([action.text])
default:
return state
}
}
// reducers/counter.js
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// reducers/index.js
import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'
export default combineReducers({
todos,
counter
})
// App.js
import { createStore } from 'redux'
import reducer from './reducers/index'
let store = createStore(reducer)
console.log(store.getState())
// {
// counter: 0,
// todos: []
// }
store.dispatch({
type: 'ADD_TODO',
text: 'Use Redux'
})
console.log(store.getState())
// {
// counter: 0,
// todos: [ 'Use Redux' ]
// }
Note the keys!
STORE just brings together ACTIONS and REDUCERS.
// ...
var store = Redux.createStore(counter);
// ...
function render() {
valueEl.innerHTML = store.getState().toString()
}
store.subscribe(render)
// ...
document.getElementById('increment')
.addEventListener('click', function () {
store.dispatch({ type: 'INCREMENT' })
});
Image via Google
Image via hpd-fotography
Image via /u/focustoinfinity