SlideShare a Scribd company logo
MAINTAINING SANITY IN A
LARGE REDUX APP
Nitish Kumar
@nitishk88
https://github.com/nitishkr8
8
CONTENT
Brief introduction to Redux
Structuring the workspace
Selectors and memoization
Leveraging a type system
Reducing Boilerplate by using helper utilities
REACT AND REDUX
https://articles.coltpini.com/react-redux-architecture-overview-
7b3e52004b6e
STRUCTURING THE WORKSPACE
Structuring workspace and
organizing files is one of the
hardest jobs in Computer Science.
Structuring a react/redux app can
be very daunting at times
Some established approaches to
organize a react/redux application
•Function First
•Feature First
FUNCTION FIRST
Function-first means that your top-level directories are named after the purpose
of the files inside. So you have: containers, components, actions, reducers, etc.
Pros:
•Isolates React from Redux - If you want to change the state management library,
you know which folders you need to touch. If you change the view library, you
can keep your redux folders intact.
Cons:
•Not scalable - As the app grows and you add more features, you add files into
the same folders. So you end up with having to scroll inside a single folder to
find your file.
•Tight coupling - The problem is also about coupling the folders together. A
single flow through your app will probably require files from all folders.
FEATURE FIRST
Feature-first means that the top-level directories are named after the main
features of the app: product, cart, session.
Pros:
•Scalable - This approach scales much better, because each new feature
comes with a new folder.
Cons:
•No separation b/w React and Redux - Changing one of them on the long run
is a very tricky job.
•Non-featured files – We end up with a folder common or shared, because
you want to reuse code across many features in your app.
MORE PATTERNS
•Ducks
•Fractal
DUCKS
Specifies a set of rules to bundle redux artifacts into isolated
module that is self contained.
Rules
MUST export default a function called reducer()
MUST export its action creators as functions
MUST have action types in the form npm-module-or-
app/reducer/ACTION_TYPE
MAY export its action types as UPPER_SNAKE_CASE, if an
external reducer needs to listen for them, or if it is a published
reusable library
EXAMPLE
// widgets.js
// Actions
const LOAD = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';
// Reducer
export default function reducer(state = {}, action = {}) {
switch (action.type) {
// do reducer stuff
default: return state;
}
}
// Action Creators
export function loadWidgets() {
return { type: LOAD };
}
export function createWidget(widget) {
return { type: CREATE, widget };
}
export function updateWidget(widget) {
return { type: UPDATE, widget };
}
export function removeWidget(widget) {
return { type: REMOVE, widget };
}
// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
return dispatch => get('/widget').then(widget =>
dispatch(updateWidget(widget)))
}
FRACTAL
https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-
4dab943092af
SELECTORS
What?
Are functions that take Redux state (, ownProps) as an argument and
return some data to pass to the component.
Why?
Can be used to compute derived data, allowing Redux to store the minimal
possible state
Smaller and decoupled React components. Performing data
transformations in the component makes them more coupled to the Redux
state and less generic/reusable
Performance Improvement
THE CASE FOR MEMOIZED
SELECTORS
Whenever a reducer returns, all connects get activated and calculates
their mapStateToProps to determine if the component needs an update.
The calculation most of the times is pretty unnecessary. This is rather
unavoidable but can be made really fast by leveraging immutable
operations and memoized selectors.
Memoized selectors are efficient. A selector is not recomputed unless one
of its arguments changes.
Selectors can be composable. They can be used as input to other
selectors.
RE-SELECT
import { createSelector } from 'reselect'
const shopItemsSelector = state =>
state.shop.items
const taxPercentSelector = state =>
state.shop.taxPercent
const subtotalSelector = createSelector(
shopItemsSelector,
items => items.reduce((acc, item) => acc +
item.value, 0)
)
const taxSelector = createSelector(
subtotalSelector,
taxPercentSelector,
(subtotal, taxPercent) => subtotal * (taxPercent /
100)
)
export const totalSelector = createSelector(
subtotalSelector,
taxSelector,
(subtotal, tax) => ({ total: subtotal + tax })
)
let exampleState = {
shop: {
taxPercent: 8,
items: [
{ name: 'apple', value: 1.20 },
{ name: 'orange', value: 0.95 },
]
}
}
console.log(subtotalSelector(exampleState)) // 2.15
console.log(taxSelector(exampleState)) // 0.172
console.log(totalSelector(exampleState)) // { total:
2.322 }
LEVERAGING FLOW
Flow is a static type checker for JavaScript. It works by analyzing your
source code, reading (and inferring) type information and making sure
there are no conflicts. It can also power a lot of IDE-like features, for
example auto-complete, jump-to-definition, etc.
Redux has three parts that could be typed:
Actions
State
Reducers
TYPING STATE
type State = {
users: Array<{
id: string,
name: string
}>,
activeUserID: string,
// ...
}
We can enforce immutability in Flow by
making every property effectively “read-
only” using “covariant” properties throughout
your state object. Flow will complain when
you try to write to any of these properties.
type State = {
+foo: string
};
let state: State = {
foo: "foo"
};
state.foo = "bar"; // Error!
TYPING ACTIONS
The base type for Redux actions is an object with a type property.
type Action = {
+type: string
}
We can define Action type to be as simple as
type Action = {
+type: string
payload: Object
}
We can do much better than using constants for action types. Let’s start by
defining all possible actions flowing through our app. We will use a union
type for this.
type Action = { type: 'LOGGED_IN', userName: string }
| { type: 'LOGGED_OUT' }
{ type: 'LOGGED_IN', userName: 'foo' } // valid
{ type: 'LOGGED_IN', login: 'foo' } // `login` is not `userName`
{ type: 'LOGGED_IN' } // no `userName`
{ type: 'TYPO_HERE' } // `type` doesn't exist
Having all possible actions typed like this is great, because it’s very clear
what can happen inside our app.
The awesome thing about Flow’s support for the tagged unions is that it can
narrow down the action type depending on your code control flow:
function user(state: State, action: Action): State {
if (action.type === 'LOGGED_IN') {
// In this `if` branch Flow narrowed down the type
// and it knows that action has `userName: string` field
return { isLoggedIn: true, name: action.userName };
}
}
TYPING ACTION CREATORS
To type the action creator, just add a return type `Action` disjoint union declared earlier
function logOut(): Action {
return { type: 'LOGGED_OUT' }
}
In case we are using custom middleware to dispatch a Promise of Action
type PromiseAction = Promise<Action>
async function logIn(login: string, pass: string): PromiseAction {
let response = await fetch(...);
// Do something...
return {
type: 'LOGGED_IN',
userName: login,
}}
TYPING THUNK ACTIONS
type Action =
| { type: "FOO", foo: number }
| { type: "BAR", bar: boolean };
type GetState = () => State;
type PromiseAction = Promise<Action>;
type ThunkAction = (dispatch: Dispatch, getState: GetState) =>
any;
type Dispatch = (action: Action | ThunkAction | PromiseAction
| Array<Action>) => any;
function foo(): ThunkAction {
return (dispatch, getState) => {
const baz = getState().baz
dispatch({ type: "BAR", bar: true })
doSomethingAsync(baz)
.then(value => {
dispatch({ type: "FOO", foo: value })
})
}
}
TYPING REDUCERS
Reducers take the state and actions that we’ve
typed and pulls them together for one method.
function reducer(state: State, action: Action): State {
// ...
}
We can also validate that every single type of action has been
handled by using the empty type in your default case.
type State = { +value: boolean };
type FooAction = { type: "FOO", foo: boolean };
type BarAction = { type: "BAR", bar: boolean };
type Action = FooAction | BarAction;
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FOO": return { ...state, value: action.foo };
case "BAR": return { ...state, value: action.bar };
default:
(action: empty);
return state;
}
}
REDUCING BOILERPLATE
The most common complaint about Redux is how it makes you write a lot of
boilerplate. We’ll see how Redux lets us choose how verbose we'd like our
code to be, depending on personal style, team preferences, longer term
maintainability, etc.
We can write our utilities for generate the following
Action creators
Reducers
MAKING ACTION CREATORS
A function that generates an action creator
function createAction(type, ...argNames) {
return function (...args) {
let action = { type }
argNames.forEach((arg, index) => {
action[argNames[index]] = args[index]
})
return action
}}
function addTodo(text) {
return {
type: 'ADD_TODO',
text
}
}
function editTodo(id, text) {
return {
type: 'EDIT_TODO',
id,
text
}
}
function
removeTodo(id) {
return {
type:
'REMOVE_TODO',
id
}
}
const addTodo = createAction ('ADD_TODO', 'text')
const editTodo = createAction ('EDIT_TODO', 'id', 'text')
const removeTodo = createAction ('REMOVE_TODO', 'id')
MAKING ASYNC ACTION CREATORS
function createThunkAction(actionType,
actionThunkCreator) {
function fn(...actionArgs) {
var thunk = actionThunkCreator(...actionArgs)
return async function(dispatch, getState) {
try {
let payload = await thunk(dispatch, getState)
dispatch({ type: actionType, payload })
} catch (error) {
dispatch({ type: actionType, payload: error, error:
true
})
throw error
}
}}
fn.toString = () => actionType
return fn}
const fetchUsersForTitle =
createThunkAction(‘FETCH_USERS’, jobTitle => {
let result
return async (dispatch, getState) => {
// fetch users
result = await UserApi.list(// ...})
return result
}
})
GENERATING REDUCERS
We can write a function that lets us express
reducers as an object mapping from action types
to handlers.
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
Then define our reducers like this
const todos = createReducer([], {
['ADD_TODO'](state, action) {
let text = action.text.trim()
return [...state, text]
},
[TOGGLE_TODO'](state, action) {
// toggle and return new state
}
})
UTILITY LIBRARIES
redux-act
An opinionated lib to create actions and reducers for Redux. The main goal is to use
actions themselves as references inside the reducers rather than string constants.
redux-actions
Utility belt for FSA-compliant actions in Redux. It provides helpers for both handling and
creating action(s)
flux-standard-action (FSA) – A human-friendly standard for Flux action objects
REDUX-ACTIONS
API
createAction(s)
handleAction(s)
createAction(type)
const increment = createAction('INCREMENT')
const decrement = createAction('DECREMENT')
increment() // { type: 'INCREMENT' }
decrement() // { type: 'DECREMENT' }
increment(10) // { type: 'INCREMENT', payload: 10
}
decrement([1, 42]) // { type: 'DECREMENT', payload: [1,
42] }
createAction(type,
payloadCreator)
const actionOne = createAction('ACTION_ONE', (key,
value) => ({ [key]: value }))
expect(actionOne('key', 1)).to.deep.equal({
type: 'ACTION_ONE',
payload: { key: 1 }
})
createActions(actionMap)
const { addTodo, removeTodo } = createActions({
ADD_TODO: todo => ({ todo }),
REMOVE_TODO: todo => ({ todo }),
})
CREATE ACTION
handleAction(type, reducer,
defaultState)
handleAction('APP/COUNTER/INCREMENT', (state, action)
=> ({
counter: state.counter + action.payload.amount,
}), defaultState)
handleAction(type, reducerMap,
defaultState)
handleAction('FETCH_DATA', {
next(state, action) {...},
throw(state, action) {...},
}, defaultState)
handleActions(reducerMap,
defaultState)
const reducer = handleActions({
INCREMENT: (state, action) => ({
counter: state.counter + action.payload
}),
DECREMENT: (state, action) => ({
counter: state.counter - action.payload
})
}, { counter: 0 })
HANDLE ACTION
THANK
YOU
REFERENCES
https://github.com/erikras/ducks-modular-redux
https://hackernoon.com/fractal-a-react-app-structure-for-infinite-
scale-4dab943092af
https://flow.org/en/docs/react/redux/
https://github.com/pauldijou/redux-act
https://github.com/reduxactions/redux-actions
https://github.com/acdlite/flux-standard-action
https://github.com/reactjs/reselect

More Related Content

What's hot (20)

PPTX
Oracle: Basic SQL
DataminingTools Inc
 
PPTX
React/Redux
Durgesh Vaishnav
 
PDF
Reactive.architecture.with.Angular
Evan Schultz
 
PDF
Rise of state_machines
Mounir Boudraa
 
PDF
Angular2 & ngrx/store: Game of States
Oren Farhi
 
PPTX
Web Developer make the most out of your Database !
Jean-Marc Desvaux
 
DOCX
Tony Vitabile .Net Portfolio
vitabile
 
PPTX
Stored procedures
Prof.Nilesh Magar
 
PDF
Refactoring in Practice - Sunnyconf 2010
Alex Sharp
 
PDF
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz
 
ODP
Ruby on rails
Mohit Jain
 
PDF
Introduction To Angular's reactive forms
Nir Kaufman
 
DOC
Framework Project Portfolio
Domingos Salvador
 
PPTX
Frank Rodenbaugh Portfolio
FrankRodenbaugh
 
PDF
Extending Redux in the Server Side
Ignacio Martín
 
PDF
Workshop 17: EmberJS parte II
Visual Engineering
 
PDF
Styling recipes for Angular components
Nir Kaufman
 
PPT
Introduction to Handoff
Harit Kothari
 
Oracle: Basic SQL
DataminingTools Inc
 
React/Redux
Durgesh Vaishnav
 
Reactive.architecture.with.Angular
Evan Schultz
 
Rise of state_machines
Mounir Boudraa
 
Angular2 & ngrx/store: Game of States
Oren Farhi
 
Web Developer make the most out of your Database !
Jean-Marc Desvaux
 
Tony Vitabile .Net Portfolio
vitabile
 
Stored procedures
Prof.Nilesh Magar
 
Refactoring in Practice - Sunnyconf 2010
Alex Sharp
 
Evan Schultz - Angular Camp - ng2-redux
Evan Schultz
 
Ruby on rails
Mohit Jain
 
Introduction To Angular's reactive forms
Nir Kaufman
 
Framework Project Portfolio
Domingos Salvador
 
Frank Rodenbaugh Portfolio
FrankRodenbaugh
 
Extending Redux in the Server Side
Ignacio Martín
 
Workshop 17: EmberJS parte II
Visual Engineering
 
Styling recipes for Angular components
Nir Kaufman
 
Introduction to Handoff
Harit Kothari
 

Similar to Maintaining sanity in a large redux app (20)

PPTX
Redux training
dasersoft
 
PDF
Materi Modern React Redux Power Point.pdf
exiabreak
 
PPTX
Getting started with Redux js
Citrix
 
PDF
How to use redux with react hooks in react native application
Katy Slemon
 
PDF
The evolution of redux action creators
George Bukhanov
 
PDF
Simple React Todo List
Ritesh Chaudhari
 
PDF
Introduction to Redux (for Angular and React devs)
Fabio Biondi
 
PPTX
How to Create a Dynamic Report in Odoo 17
Celine George
 
PDF
React state managmenet with Redux
Vedran Blaženka
 
PDF
Pieter De Baets - An introduction to React Native
tlv-ios-dev
 
PDF
Redux. From twitter hype to production
FDConf
 
PDF
Stay with React.js in 2020
Jerry Liao
 
PDF
React redux
Michel Perez
 
PDF
Workshop 20: ReactJS Part II Flux Pattern & Redux
Visual Engineering
 
PDF
Understanding redux
David Atchley
 
PPTX
Reducers+flux=redux
Shmulik Chicvashvili
 
PDF
Intro to Redux | DreamLab Academy #3
DreamLab
 
PDF
Redux. From twitter hype to production
Jenya Terpil
 
PDF
Reactивная тяга
Vitebsk Miniq
 
PPTX
Client Actions In Odoo 17 - Odoo 17 Slides
Celine George
 
Redux training
dasersoft
 
Materi Modern React Redux Power Point.pdf
exiabreak
 
Getting started with Redux js
Citrix
 
How to use redux with react hooks in react native application
Katy Slemon
 
The evolution of redux action creators
George Bukhanov
 
Simple React Todo List
Ritesh Chaudhari
 
Introduction to Redux (for Angular and React devs)
Fabio Biondi
 
How to Create a Dynamic Report in Odoo 17
Celine George
 
React state managmenet with Redux
Vedran Blaženka
 
Pieter De Baets - An introduction to React Native
tlv-ios-dev
 
Redux. From twitter hype to production
FDConf
 
Stay with React.js in 2020
Jerry Liao
 
React redux
Michel Perez
 
Workshop 20: ReactJS Part II Flux Pattern & Redux
Visual Engineering
 
Understanding redux
David Atchley
 
Reducers+flux=redux
Shmulik Chicvashvili
 
Intro to Redux | DreamLab Academy #3
DreamLab
 
Redux. From twitter hype to production
Jenya Terpil
 
Reactивная тяга
Vitebsk Miniq
 
Client Actions In Odoo 17 - Odoo 17 Slides
Celine George
 
Ad

Recently uploaded (20)

PDF
Introduction to Ship Engine Room Systems.pdf
Mahmoud Moghtaderi
 
PPTX
Information Retrieval and Extraction - Module 7
premSankar19
 
PPTX
Precedence and Associativity in C prog. language
Mahendra Dheer
 
PDF
67243-Cooling and Heating & Calculation.pdf
DHAKA POLYTECHNIC
 
PPTX
Ground improvement techniques-DEWATERING
DivakarSai4
 
PDF
STUDY OF NOVEL CHANNEL MATERIALS USING III-V COMPOUNDS WITH VARIOUS GATE DIEL...
ijoejnl
 
PDF
EVS+PRESENTATIONS EVS+PRESENTATIONS like
saiyedaqib429
 
PPTX
ETP Presentation(1000m3 Small ETP For Power Plant and industry
MD Azharul Islam
 
PPTX
Water resources Engineering GIS KRT.pptx
Krunal Thanki
 
PDF
AI-Driven IoT-Enabled UAV Inspection Framework for Predictive Maintenance and...
ijcncjournal019
 
PPTX
filteration _ pre.pptx 11111110001.pptx
awasthivaibhav825
 
PDF
Zero Carbon Building Performance standard
BassemOsman1
 
PPTX
ENSA_Module_7.pptx_wide_area_network_concepts
RanaMukherjee24
 
PPTX
Inventory management chapter in automation and robotics.
atisht0104
 
PDF
All chapters of Strength of materials.ppt
girmabiniyam1234
 
PPTX
Introduction to Fluid and Thermal Engineering
Avesahemad Husainy
 
PDF
Machine Learning All topics Covers In This Single Slides
AmritTiwari19
 
PPTX
sunil mishra pptmmmmmmmmmmmmmmmmmmmmmmmmm
singhamit111
 
PDF
Air -Powered Car PPT by ER. SHRESTH SUDHIR KOKNE.pdf
SHRESTHKOKNE
 
PPTX
22PCOAM21 Session 1 Data Management.pptx
Guru Nanak Technical Institutions
 
Introduction to Ship Engine Room Systems.pdf
Mahmoud Moghtaderi
 
Information Retrieval and Extraction - Module 7
premSankar19
 
Precedence and Associativity in C prog. language
Mahendra Dheer
 
67243-Cooling and Heating & Calculation.pdf
DHAKA POLYTECHNIC
 
Ground improvement techniques-DEWATERING
DivakarSai4
 
STUDY OF NOVEL CHANNEL MATERIALS USING III-V COMPOUNDS WITH VARIOUS GATE DIEL...
ijoejnl
 
EVS+PRESENTATIONS EVS+PRESENTATIONS like
saiyedaqib429
 
ETP Presentation(1000m3 Small ETP For Power Plant and industry
MD Azharul Islam
 
Water resources Engineering GIS KRT.pptx
Krunal Thanki
 
AI-Driven IoT-Enabled UAV Inspection Framework for Predictive Maintenance and...
ijcncjournal019
 
filteration _ pre.pptx 11111110001.pptx
awasthivaibhav825
 
Zero Carbon Building Performance standard
BassemOsman1
 
ENSA_Module_7.pptx_wide_area_network_concepts
RanaMukherjee24
 
Inventory management chapter in automation and robotics.
atisht0104
 
All chapters of Strength of materials.ppt
girmabiniyam1234
 
Introduction to Fluid and Thermal Engineering
Avesahemad Husainy
 
Machine Learning All topics Covers In This Single Slides
AmritTiwari19
 
sunil mishra pptmmmmmmmmmmmmmmmmmmmmmmmmm
singhamit111
 
Air -Powered Car PPT by ER. SHRESTH SUDHIR KOKNE.pdf
SHRESTHKOKNE
 
22PCOAM21 Session 1 Data Management.pptx
Guru Nanak Technical Institutions
 
Ad

Maintaining sanity in a large redux app

  • 1. MAINTAINING SANITY IN A LARGE REDUX APP Nitish Kumar @nitishk88 https://github.com/nitishkr8 8
  • 2. CONTENT Brief introduction to Redux Structuring the workspace Selectors and memoization Leveraging a type system Reducing Boilerplate by using helper utilities
  • 4. STRUCTURING THE WORKSPACE Structuring workspace and organizing files is one of the hardest jobs in Computer Science. Structuring a react/redux app can be very daunting at times Some established approaches to organize a react/redux application •Function First •Feature First
  • 5. FUNCTION FIRST Function-first means that your top-level directories are named after the purpose of the files inside. So you have: containers, components, actions, reducers, etc. Pros: •Isolates React from Redux - If you want to change the state management library, you know which folders you need to touch. If you change the view library, you can keep your redux folders intact. Cons: •Not scalable - As the app grows and you add more features, you add files into the same folders. So you end up with having to scroll inside a single folder to find your file. •Tight coupling - The problem is also about coupling the folders together. A single flow through your app will probably require files from all folders.
  • 6. FEATURE FIRST Feature-first means that the top-level directories are named after the main features of the app: product, cart, session. Pros: •Scalable - This approach scales much better, because each new feature comes with a new folder. Cons: •No separation b/w React and Redux - Changing one of them on the long run is a very tricky job. •Non-featured files – We end up with a folder common or shared, because you want to reuse code across many features in your app.
  • 8. DUCKS Specifies a set of rules to bundle redux artifacts into isolated module that is self contained. Rules MUST export default a function called reducer() MUST export its action creators as functions MUST have action types in the form npm-module-or- app/reducer/ACTION_TYPE MAY export its action types as UPPER_SNAKE_CASE, if an external reducer needs to listen for them, or if it is a published reusable library
  • 9. EXAMPLE // widgets.js // Actions const LOAD = 'my-app/widgets/LOAD'; const CREATE = 'my-app/widgets/CREATE'; const UPDATE = 'my-app/widgets/UPDATE'; const REMOVE = 'my-app/widgets/REMOVE'; // Reducer export default function reducer(state = {}, action = {}) { switch (action.type) { // do reducer stuff default: return state; } } // Action Creators export function loadWidgets() { return { type: LOAD }; } export function createWidget(widget) { return { type: CREATE, widget }; } export function updateWidget(widget) { return { type: UPDATE, widget }; } export function removeWidget(widget) { return { type: REMOVE, widget }; } // side effects, only as applicable // e.g. thunks, epics, etc export function getWidget () { return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget))) }
  • 11. SELECTORS What? Are functions that take Redux state (, ownProps) as an argument and return some data to pass to the component. Why? Can be used to compute derived data, allowing Redux to store the minimal possible state Smaller and decoupled React components. Performing data transformations in the component makes them more coupled to the Redux state and less generic/reusable Performance Improvement
  • 12. THE CASE FOR MEMOIZED SELECTORS Whenever a reducer returns, all connects get activated and calculates their mapStateToProps to determine if the component needs an update. The calculation most of the times is pretty unnecessary. This is rather unavoidable but can be made really fast by leveraging immutable operations and memoized selectors. Memoized selectors are efficient. A selector is not recomputed unless one of its arguments changes. Selectors can be composable. They can be used as input to other selectors.
  • 13. RE-SELECT import { createSelector } from 'reselect' const shopItemsSelector = state => state.shop.items const taxPercentSelector = state => state.shop.taxPercent const subtotalSelector = createSelector( shopItemsSelector, items => items.reduce((acc, item) => acc + item.value, 0) ) const taxSelector = createSelector( subtotalSelector, taxPercentSelector, (subtotal, taxPercent) => subtotal * (taxPercent / 100) ) export const totalSelector = createSelector( subtotalSelector, taxSelector, (subtotal, tax) => ({ total: subtotal + tax }) ) let exampleState = { shop: { taxPercent: 8, items: [ { name: 'apple', value: 1.20 }, { name: 'orange', value: 0.95 }, ] } } console.log(subtotalSelector(exampleState)) // 2.15 console.log(taxSelector(exampleState)) // 0.172 console.log(totalSelector(exampleState)) // { total: 2.322 }
  • 14. LEVERAGING FLOW Flow is a static type checker for JavaScript. It works by analyzing your source code, reading (and inferring) type information and making sure there are no conflicts. It can also power a lot of IDE-like features, for example auto-complete, jump-to-definition, etc. Redux has three parts that could be typed: Actions State Reducers
  • 15. TYPING STATE type State = { users: Array<{ id: string, name: string }>, activeUserID: string, // ... } We can enforce immutability in Flow by making every property effectively “read- only” using “covariant” properties throughout your state object. Flow will complain when you try to write to any of these properties. type State = { +foo: string }; let state: State = { foo: "foo" }; state.foo = "bar"; // Error!
  • 16. TYPING ACTIONS The base type for Redux actions is an object with a type property. type Action = { +type: string } We can define Action type to be as simple as type Action = { +type: string payload: Object } We can do much better than using constants for action types. Let’s start by defining all possible actions flowing through our app. We will use a union type for this. type Action = { type: 'LOGGED_IN', userName: string } | { type: 'LOGGED_OUT' } { type: 'LOGGED_IN', userName: 'foo' } // valid { type: 'LOGGED_IN', login: 'foo' } // `login` is not `userName` { type: 'LOGGED_IN' } // no `userName` { type: 'TYPO_HERE' } // `type` doesn't exist
  • 17. Having all possible actions typed like this is great, because it’s very clear what can happen inside our app. The awesome thing about Flow’s support for the tagged unions is that it can narrow down the action type depending on your code control flow: function user(state: State, action: Action): State { if (action.type === 'LOGGED_IN') { // In this `if` branch Flow narrowed down the type // and it knows that action has `userName: string` field return { isLoggedIn: true, name: action.userName }; } }
  • 18. TYPING ACTION CREATORS To type the action creator, just add a return type `Action` disjoint union declared earlier function logOut(): Action { return { type: 'LOGGED_OUT' } } In case we are using custom middleware to dispatch a Promise of Action type PromiseAction = Promise<Action> async function logIn(login: string, pass: string): PromiseAction { let response = await fetch(...); // Do something... return { type: 'LOGGED_IN', userName: login, }}
  • 19. TYPING THUNK ACTIONS type Action = | { type: "FOO", foo: number } | { type: "BAR", bar: boolean }; type GetState = () => State; type PromiseAction = Promise<Action>; type ThunkAction = (dispatch: Dispatch, getState: GetState) => any; type Dispatch = (action: Action | ThunkAction | PromiseAction | Array<Action>) => any; function foo(): ThunkAction { return (dispatch, getState) => { const baz = getState().baz dispatch({ type: "BAR", bar: true }) doSomethingAsync(baz) .then(value => { dispatch({ type: "FOO", foo: value }) }) } }
  • 20. TYPING REDUCERS Reducers take the state and actions that we’ve typed and pulls them together for one method. function reducer(state: State, action: Action): State { // ... } We can also validate that every single type of action has been handled by using the empty type in your default case. type State = { +value: boolean }; type FooAction = { type: "FOO", foo: boolean }; type BarAction = { type: "BAR", bar: boolean }; type Action = FooAction | BarAction; function reducer(state: State, action: Action): State { switch (action.type) { case "FOO": return { ...state, value: action.foo }; case "BAR": return { ...state, value: action.bar }; default: (action: empty); return state; } }
  • 21. REDUCING BOILERPLATE The most common complaint about Redux is how it makes you write a lot of boilerplate. We’ll see how Redux lets us choose how verbose we'd like our code to be, depending on personal style, team preferences, longer term maintainability, etc. We can write our utilities for generate the following Action creators Reducers
  • 22. MAKING ACTION CREATORS A function that generates an action creator function createAction(type, ...argNames) { return function (...args) { let action = { type } argNames.forEach((arg, index) => { action[argNames[index]] = args[index] }) return action }}
  • 23. function addTodo(text) { return { type: 'ADD_TODO', text } } function editTodo(id, text) { return { type: 'EDIT_TODO', id, text } } function removeTodo(id) { return { type: 'REMOVE_TODO', id } } const addTodo = createAction ('ADD_TODO', 'text') const editTodo = createAction ('EDIT_TODO', 'id', 'text') const removeTodo = createAction ('REMOVE_TODO', 'id')
  • 24. MAKING ASYNC ACTION CREATORS function createThunkAction(actionType, actionThunkCreator) { function fn(...actionArgs) { var thunk = actionThunkCreator(...actionArgs) return async function(dispatch, getState) { try { let payload = await thunk(dispatch, getState) dispatch({ type: actionType, payload }) } catch (error) { dispatch({ type: actionType, payload: error, error: true }) throw error } }} fn.toString = () => actionType return fn} const fetchUsersForTitle = createThunkAction(‘FETCH_USERS’, jobTitle => { let result return async (dispatch, getState) => { // fetch users result = await UserApi.list(// ...}) return result } })
  • 25. GENERATING REDUCERS We can write a function that lets us express reducers as an object mapping from action types to handlers. function createReducer(initialState, handlers) { return function reducer(state = initialState, action) { if (handlers.hasOwnProperty(action.type)) { return handlers[action.type](state, action) } else { return state } } } Then define our reducers like this const todos = createReducer([], { ['ADD_TODO'](state, action) { let text = action.text.trim() return [...state, text] }, [TOGGLE_TODO'](state, action) { // toggle and return new state } })
  • 26. UTILITY LIBRARIES redux-act An opinionated lib to create actions and reducers for Redux. The main goal is to use actions themselves as references inside the reducers rather than string constants. redux-actions Utility belt for FSA-compliant actions in Redux. It provides helpers for both handling and creating action(s) flux-standard-action (FSA) – A human-friendly standard for Flux action objects
  • 28. createAction(type) const increment = createAction('INCREMENT') const decrement = createAction('DECREMENT') increment() // { type: 'INCREMENT' } decrement() // { type: 'DECREMENT' } increment(10) // { type: 'INCREMENT', payload: 10 } decrement([1, 42]) // { type: 'DECREMENT', payload: [1, 42] } createAction(type, payloadCreator) const actionOne = createAction('ACTION_ONE', (key, value) => ({ [key]: value })) expect(actionOne('key', 1)).to.deep.equal({ type: 'ACTION_ONE', payload: { key: 1 } }) createActions(actionMap) const { addTodo, removeTodo } = createActions({ ADD_TODO: todo => ({ todo }), REMOVE_TODO: todo => ({ todo }), }) CREATE ACTION
  • 29. handleAction(type, reducer, defaultState) handleAction('APP/COUNTER/INCREMENT', (state, action) => ({ counter: state.counter + action.payload.amount, }), defaultState) handleAction(type, reducerMap, defaultState) handleAction('FETCH_DATA', { next(state, action) {...}, throw(state, action) {...}, }, defaultState) handleActions(reducerMap, defaultState) const reducer = handleActions({ INCREMENT: (state, action) => ({ counter: state.counter + action.payload }), DECREMENT: (state, action) => ({ counter: state.counter - action.payload }) }, { counter: 0 }) HANDLE ACTION