Redux	Deep	Dive
Destructuring an	Object
1. const newState = {
2. ...state,
3. key1: value1,
4. key2: value2
5. }
1. const newState = _.extend(
2. {},
3. state,
4. {
5. key1, value1,
6. key2, value2
7. }
8. )
Agenda
1. Redux	Basics	(Brief)
2. Enhancers,	Middlewares
3. Problems
1. Typos,	Types,	Tree	Shaking,	Chunking
2. Partial	but	failing	Solutions
4. Solution
React’s Philosophy
Props	/	
State
VDOM DOM
render
Pure	Function
React
React	Philosophy
Events
Props	/	
State
VDOM DOM
render
Pure	Function
ReactsetState
As	your	App	grows,	it	
becomes	difficult	to	
manage	using	setState
Enter	Redux
Redux Store
State
Component
Action
Reducers
Provide	store	at	root	level
1. import { createStore } from 'redux'
2. import { Provider } from 'react-redux';
3.
4. import reducer from './reducers';
5.
6. const store = createStore(reducer);
7.
8. export default () => {
9. return (
10. <Provider store={store}>
11. <App>
12. </Provider>
13. )
14. };
Store’s	State	passed	to	Components
1. import { connect } from 'react-redux'
2.
3. class HeaderComponent extends React.Component {
4. render () {
5. return (
6. <h1> {this.props.user} </h1>
7. )
8. }
9. }
10.
11. const mapStateToProps = (state) => ({
12. user: state.user
13. });
14.
15. export default connect(mapStateToProps)(HeaderComponent);
Dispatch	an	Action
1. class Counter extends React.Component {
2. onClick = (e) => {
3. this.props.dispatch({
4. type: 'COUNTER_CLICKED',
5. payload: e.currentTarget.data.id
6. })
7. }
8. render () {
9. return <div
10. onClick={this.onClick(e)}
11. data-id={this.props.id}
12. />
13. }
14. }
Reducer
1. export default function reducer(state, action) {
2. switch (action.type) {
3. case 'COUNTER_CLICKED':
4. return {
5. ...state,
6. counterClicked: true
7. }
8. ...
9. default:
10. return state;
11. }
12.}
Redux Store
State
Component
Action
Reducers
Time	to	Level	Up
Enhancers
createStore => newCreateStore
Enhancers	wrap	around	createStore
enhancer
createStore
1. function enhancer (createStore) {
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.}
Sample	Enhancer	which	logs	on	every	getState()
1. function enhancer (createStore) {
2. return (...args) => {
3.
4.
5.
6.
7.
8.
9.
10.
11. }
12.}
Sample	Enhancer	which	logs	on	every	getState()
1. function enhancer (createStore) {
2. return (...args) => {
3. const store = createStore(...args)
4. return {
5. ...store,
6.
7.
8.
9.
10. }
11. }
12.}
Sample	Enhancer	which	logs	on	every	getState()
1. function enhancer (createStore) {
2. return (...args) => {
3. const store = createStore(...args)
4. return {
5. ...store,
6. getState: () => {
7. console.log('getState was called')
8. return store.getState()
9. }
10. }
11. }
12.}
Sample	Enhancer	which	logs	on	every	getState()
How	to	use	Enhancers
1. const store = enchancer(createStore)(reducer)
OR
1. const store = createStore(reducer, enhancer)
Multiple	Enhancers
1. const store = enhancer1(enhancer2(createStore))(reducer)
OR
1. const store = compose(enhacer1, enhancer2)(createStore)(reducer)
OR
1. const store = createStore(reducer, compose(enchancer1, enhancer2))
(( )))compose( first , second , third )( ...args( )
Art	by	C
Debugging	– Redux Dev	Tools
applyMiddleware
1. export default function applyMiddleware(...middlewares) {
2. return (createStore) => (...args) => {
3. const store = createStore(...args)
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14. }
15. }
applyMiddleware is	an	Enhancer
1. export default function applyMiddleware(...middlewares) {
2. return (createStore) => (...args) => {
3. const store = createStore(...args)
4. let dispatch = () => {}
5. const newStore = {
6. ...store,
7. dispatch: (...action) => dispatch(...action)
8. }
9.
10.
11.
12.
13. return newStore
14. }
15. }
applyMiddleware is	an	Enhancer
1. export default function applyMiddleware(...middlewares) {
2. return (createStore) => (...args) => {
3. const store = createStore(...args)
4. let dispatch = () => {}
5. const newStore = {
6. ...store,
7. dispatch: (...action) => dispatch(...action)
8. }
9.
10. const chain = middlewares.map(middleware => middleware(newStore)) // init with store
11. dispatch = compose(...chain)(store.dispatch) // load with next dispatch
12.
13. return newStore
14. }
15. }
Redux Middlewares
store => nextMiddleware => action => nextMiddleware(action)
Redux	Thunk
1. const thunk = store => next => action => {
2. if (typeof action === 'function') {
3. return action(store.dispatch, store.getState)
4. }
5.
6. return next(action)
7. }
Redux	Thunk Usage
1. function doSomethingAction (dispatch) {
2. dispatch((dispatch, getState) => {
3.
4.
5.
6.
7.
8. })
9. }
Redux	Thunk Usage
1. function doSomethingAction (dispatch) {
2. dispatch((dispatch, getState) => {
3. const state = getState()
4. dispatch({
5. type: 'DO_SOMETHING',
6. payload: state.user
7. })
8. })
9. }
Non	DOM	Side	Effects
1. const setTitle = store => next => action => {
2. const getState = store.getState
3.
4. const oldTitle = getState().title
5. next(action)
6. const newTitle = getState().title
7.
8. if (oldTitle !== newTitle) {
9. document.title = newTitle
10. }
11. return action
12.}
Tracking	Breadcrumbs	/	User	Flow
1. import Raven from '../helpers/sentry’
2.
3. const trackBreadCrumb = () => next => action => {
4. Raven.captureBreadcrumb({
5. category: 'redux'
6. message: action.type
7. })
8. return next(action)
9. }
Sentry	Error	Logs	Breadcrumbs
How	to	use	these	middlewares
1. import { createStore, applyMiddleware } from 'redux'
2.
const store = createStore(
3. reducer,
4. compose(
5. applyMiddleware(
6. thunk,
7. setTitle,
8. trackBreadcrumb
9. ),
10. window.__REDUX_DEVTOOLS_EXTENSION__()
11. )
12. )
Redux-sagas
For	more	info	attend	Preeti’s talk	after	lunch.
My	Problems
My	Problems
^
1.	Typos
Typos
1. dispatch({
2. type: 'DO_SOEMTHING_AMAMAZING'
3. })
export const AMAZING_ACTION = 'AMAZING_ACTION’ // actionsList.js
1. import {AMAZING_ACTION} from
'./actionsList’
2.
3. dispatch({
4. type: AMAZING_ACTION
5. })
1. import {AMAZING_ACTION} from
'../actionsList’
2.
3. export function reducer (state, action) {
4. switch (action.type) {
5. case AMAZING_ACTION:
6. ...
7. }
8. }
// actionsList.js
1. export const AMAZING_ACTION = 'AMAZING_ACTION'
2. export const AWESOME_ACTION = 'AWESOME_ACTION'
3. export const FANTASTIC_ACTION = 'FANTASTIC_ACTION'
4. export const MINDBLOWING_ACTION = 'MINDBLOWING_ACTION'
5. export const ACTION_ACTION = 'ACTION_ACTION'
6. export const DRAMA_ACTION = 'DRAMA_ACTION'
7. export const AMAZING_ACTION_2 = 'AMAZING_ACTION_2'
8. export const AWESOME_ACTION_2 = 'AWESOME_ACTION_2'
9. export const FANTASTIC_ACTION_2 = 'FANTASTIC_ACTION_2'
10. export const MINDBLOWING_ACTION_2 = 'MINDBLOWING_ACTION_2'
11. export const ACTION_ACTION_2 = 'ACTION_ACTION_2'
12. export const DRAMA_ACTION_2 = 'DRAMA_ACTION_2'
13. export const AMAZING_ACTION_3 = 'AMAZING_ACTION_3'
14. export const AWESOME_ACTION_3 = 'AWESOME_ACTION_3'
15. export const FANTASTIC_ACTION_3 = 'FANTASTIC_ACTION_3'
16. export const MINDBLOWING_ACTION_3 = 'MINDBLOWING_ACTION_3'
17. export const ACTION_ACTION_3 = 'ACTION_ACTION_3'
18. export const DRAMA_ACTION_3 = 'DRAMA_ACTION_3’
Need	to	add	in	3	places	instead	of	2	😩
1.Actions
2.Reducers
3.ActionsList
2.	Types
Types	- Action
1. dispatch({
2. type: 'ACTION_DRAMA',
3. payload: 8
4. })
Types	- Action
1. dispatch({
2. type: 'ACTION_DRAMA',
3. payload: {
4. id: 8,
5. rating: 9.5
6. }
7. })
Types	- Reducer
1. function reducer (state, action) {
2. switch (action.type) {
3. case 'ACTION_DRAMA':
4. const id = action.payload
5.
6. ...
7.
8. }
9. }
Types	- Reducer
1. function reducer (state, action) {
2. switch (action.type) {
3. case 'ACTION_DRAMA':
4. const {id, rating} = action.payload
5.
6. ...
7.
8. }
9. }
In	another	location,	you	forgot
1. dispatch({
2. type: 'ACTION_DRAMA',
3. payload: 8
4. })
How	are	you	going	to	grep /	search	this?
1. const ACTION = 'ACTION_'
2. dispatch({
3. type: ACTION + 'DRAMA',
4. payload: 8
5. })
Image	by	Dave	Dugdale of	www.learningDSLRVideo.com
Action	Types
1. export type DramaAction = {
2. type: 'DRAMA_ACTION',
3. payload: {
4. id: number,
5. rating: number
6. },
7. };
8.
9. export type ActionAction = {
10. type: 'ACTION_ACTION',
11. payload: number,
12. };
13.
14. export type Action = DramaAction | ActionAction;
Store	Types
1. import type {
2. Store as ReduxStore,
3. Dispatch as ReduxDispatch,
4. } from 'redux';
5. import type { Action } from './Action';
6. import type { State } from './State';
7.
8. export type Store = ReduxStore<State, Action>;
9.
10. export type GetState = () => State;
11.
12. export type Dispatch = ReduxDispatch<Action>
13.
Action
1. import type {Dispatch} from './types/Store'
2.
export function dramaAction(dispatch: Dispatch, id: number, rating: number) {
3. return dispatch({
4. type: 'DRAMA_ACTION',
5. payload: { id, rating }
6. });
7. }
8.
export function actionAction(dispatch: Dispatch, id: number) {
9. return dispatch({
10. type: 'ACTION_ACTION',
11. payload: id
12. })
13. }
Need	to	maintain	3	places	instead	of	2	😩
1.Actions
2.Reducers
3.ActionTypes
Too	much	work,	must	find	hack.
1. export type FallbackAction = {
2. type: string,
3. [string]: any
4. }
5.
6. export type Action = DramaAction | ActionAction | FallbackAction;
3.	Tree	Shaking
Removing	unused	code	during	build	or	minification
Long	list	of	Switch	Case	in	Reducer
1. export default function reducer(state, action) {
2. switch (action.type) {
3. case 'DRAMA_ACTION':
4. return {
5. ...state,
6. movie: {id: action.id, rating: action.rating}
7. }
8. case 'ACTION_ACTION':
9. return {
10. ...state,
11. movie: {id: action.id}
12. }
13. default:
14. return state;
15. }
16. }
Tree	Shaking	Dead	Code
becomes	difficult
4.	Route	Specific	Code	Splitting	
Reducers
Pinterest’s	Chunks
Possibly	even	Reducers
Pinterest’s	Chunks
Route	Specific	Reducers
store.replaceReducer
1. import globalReducer from './reducers/globalReducer'
2.
function onRouteChange (newReducer) {
3. store.replaceReducer(
4. mergeReducer(
5. globalReducer,
6. newReducer
7. )
8. )
9. }
How	to	go	ahead	with	Route	Specific	Reducers
1. Each	Chunk	defines	its	reducer
2. Before	component	is	rendered,	call	
store.replaceReducer
Problems	with	this	approach
1. Difficult	to	migrate.
2. Difficult	to	ensure	that	the	correct	reducer	will	be	
available	when	you	dispatch	an	action.
Photo	by	Riccardo	Annandale on	Unsplash
Dispatch	your	reducer
Dispatch	your	reducer	instead	of	type
1. import {ACTION_DRAMA} from './dramaReducers'
2.
export function dramaAction (dispatch, id, rating) {
3. dispatch({
4. reducer: ACTION_DRAMA,
5. payload: {
6. id,
7. rating
8. }
9. })
10.}
Reducer
1. export function ACTION_DRAMA (state, payload) {
2. return {
3. ...state,
4. movie: {
5. id: payload.id,
6. rating: payload.rating
7. }
8. }
9. }
But	does	it	fix	my	problems?
1.	Typos
1. import { ACTION_DRAMA } from './dramaReducers'
2.
export function dramaAction (dispatch, id, rating) {
3. dispatch({
4. reducer: ACTION_DRAMA,
5. payload: {
6. id,
7. rating
8. }
9. })
10.}
2.	Types
1. import type { State } from './State';
2.
3. export type Reducer<P> = (
4. state: State,
5. payload: P
6. ) => State;
7.
8. export type Action<P> = {
9. reducer: Reducer<P>,
10. payload: P
11. }
12.
13. export type Dispatch = <T>(action: Action<T>) => void
2.	Types	- Reducer
1. export function INCREMENT_COUNTER (state: State, payload: number): State {
2. return {
3. ...state,
4. counter: state.counter + payload
5. }
6. }
2.	Types	- Action
1. export function increment(dispatch: Dispatch,
amount: number) {
2. return dispatch({
3. reducer: INCREMENT_COUNTER,
4. payload: 1,
5. });
6. }
7.
2.	Types	- IDE	(Nuclide)	throws	errors
3.	Tree	Shaking	Unused	Reducer	Code
1. import { ACTION_DRAMA } from './dramaReducers'
2.
export function dramaAction (dispatch, id, rating) {
3. dispatch({
4. reducer: ACTION_DRAMA,
5. payload: {
6. id,
7. rating
8. }
9. })
10.}
3.	Tree	Shaking	Unused	Reducer	Code
1. https://github.com/kentcdodds/webpack-tree-shaking-exports
4.	Code	Splitting	across	Routes	- Before
Entry
Store
Reducers Middlewares
Component
Actions
4.	Code	Splitting	across	Routes	- After
Entry
Store
Middlewares
Component
Actions
Reducers
How	to	get	this	to	work?
1. const dispatchReducerMiddleware = () => next => action => {
2. if (
3. typeof action === 'object' &&
4. typeof action.reducer === 'function'
5. ) {
6. action = {
7. ...action,
8. type: action.reducer.name
9. }
10. }
11. return next(action)
12.}
What	about	the	reducer	passed	to	createStore?
1. function reducer (state, action) {
2. if (action.reducer) {
3. return action.reducer(state, action.payload)
4. }
5. return state
6. }
How	to	use	with	Redux	Devtools
1. const store = createStore(
2. reducer,
3. compose(
4. applyMiddleware(
5. ...middlewares,
6. dispatchReducerMiddleware
7. ),
8. window.__REDUX_DEVTOOLS_EXTENSION__()
9. )
10.)
combineReducers
Left	as	an	exercise	to	the	Reader	of
https://github.com/azizhk/react-boilerplate/pull/1
Codemod
https://gist.github.com/azizhk/b4f9f5e45055a25bd28eef56540714e4
Takeaway
1. Write	your	enhancer	for	your	problems.
2. Write	codemods for	migrations
Aziz	Khambati
/azizhk110
/@azizhk

Redux Deep Dive - ReactFoo Pune 2018