Slide 1

Slide 1 text

Simplify State Management with Redux Jonathan Kemp @jonkemp

Slide 2

Slide 2 text

Scripps Networks HGTV · DIY Network · Food Network · Cooking Channel · Travel Channel www.scrippsnetworksinteractive.com/careers

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Managing State in single page applications

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Redux

Slide 7

Slide 7 text

Make state mutations predictable

Slide 8

Slide 8 text

Three Principles of Redux 1. Single source of truth 2. State is read-only 3. Changes are made with pure functions

Slide 9

Slide 9 text

Single source of truth 1. The state of your whole application is stored in an object tree within a single store. 2. A single state tree also makes it easier to debug or introspect an application.

Slide 10

Slide 10 text

State is read-only - The only way to mutate the state is to emit an action, an object describing what happened. - This ensures that neither the views nor the network callbacks will ever write directly to the state.

Slide 11

Slide 11 text

Changes are made with pure functions - To specify how the state tree is transformed by actions, you write pure reducers.

Slide 12

Slide 12 text

Deterministic View Render

Slide 13

Slide 13 text

Pure Functions

Slide 14

Slide 14 text

Pure Functions - Given the same input, always returns the same output. - Do Not Cause Side Effects. - Don’t Mutate Arguments.

Slide 15

Slide 15 text

Pure Functions 1. Avoid globals A. Ensure all its data comes from inputs and it never reaches outside itself 2. Don’t modify input 3. Avoid side-effects

Slide 16

Slide 16 text

Keep Reducers Pure

Slide 17

Slide 17 text

function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } let store = createStore(counter); store.subscribe(() => console.log(store.getState()) );

Slide 18

Slide 18 text

function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } let store = createStore(counter); store.subscribe(() => console.log(store.getState()) );

Slide 19

Slide 19 text

function counter(state = 0, action) { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } let store = createStore(counter); store.subscribe(() => console.log(store.getState()) );

Slide 20

Slide 20 text

/* Dispatches actions */ 
 store.dispatch() /* Listens to dispatched actions */ 
 store.subscribe() /* Get state from store */ 
 store.getState()

Slide 21

Slide 21 text

store.dispatch({ type: 'INCREMENT' }) // 1 store.dispatch({ type: 'INCREMENT' }) // 2 store.dispatch({ type: 'DECREMENT' }) // 1

Slide 22

Slide 22 text

React Redux

Slide 23

Slide 23 text

Presentational and Container Components

Slide 24

Slide 24 text

Get started with Redux 1. Can be used with any view library, independent of React 2. Babel or module bundler not required 3. Install via npm

Slide 25

Slide 25 text

React Slingshot React, Redux, React Router Tooling: Babel, WEbpack, ESLint, Unit Testing, ETC.

Slide 26

Slide 26 text

Recalculator

Slide 27

Slide 27 text

Features: I. History tape a. Every calculation gets saved to the history tape b. Refer to calculations later or send them right back to the calculator c. Allows you to see everything you have typed

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

{ items: [ { id: 1, expression: "5 * 3", result: 15 }, { id: 0, expression: "5 * 2", result: 10 } ], lastResult: 15 }

Slide 32

Slide 32 text

import { connect } from 'react-redux'; import { addEntry } from '../actions/index'; import EntryForm from ‘../components/EntryForm'; const mapStateToProps = (state) => { return { result: state.lastResult } }; const AddEntry = connect( mapStateToProps, mapDispatchToProps )(EntryForm);

Slide 33

Slide 33 text

import { connect } from 'react-redux'; import { addEntry } from '../actions/index'; import EntryForm from ‘../components/EntryForm'; const mapDispatchToProps = (dispatch) => { return { submit: (value) => { dispatch(addEntry(value)); } } } const AddEntry = connect( mapStateToProps, mapDispatchToProps )(EntryForm);

Slide 34

Slide 34 text

import { connect } from 'react-redux'; import { addEntry } from '../actions/index'; import EntryForm from ‘../components/EntryForm'; const mapDispatchToProps = (dispatch) => { return { submit: (value) => { dispatch(addEntry(value)); } } } const AddEntry = connect( mapStateToProps, mapDispatchToProps )(EntryForm);

Slide 35

Slide 35 text

import { connect } from 'react-redux'; import { addEntry } from '../actions/index'; import EntryForm from ‘../components/EntryForm'; const mapDispatchToProps = (dispatch) => { return { submit: (value) => { dispatch(addEntry(value)); } } } const AddEntry = connect( mapStateToProps, mapDispatchToProps )(EntryForm);

Slide 36

Slide 36 text

render() { return ( { e.preventDefault(); this.props.submit(this.input.value); this.input.value = ''; }}>
{ this.input = node }} onChange={this.handleChange} />
Submit ); }

Slide 37

Slide 37 text

render() { return ( { e.preventDefault(); this.props.submit(this.input.value); this.input.value = ''; }}>
{ this.input = node }} onChange={this.handleChange} />
Submit ); }

Slide 38

Slide 38 text

/* * action types */ export const ADD_ENTRY = 'ADD_ENTRY'; /* * action creators */ export function addEntry(text) { return { type: ADD_ENTRY, text }; }

Slide 39

Slide 39 text

function calculateApp(state, action) { switch (action.type) { case ADD_ENTRY: return Object.assign({}, state, { items: [ { id: state.items.reduce((maxId, item) => Math.max(item.id, maxId), -1) + 1, expression: action.text, result: eval(action.text) }, ...state.items ] }) default: return state } }

Slide 40

Slide 40 text

function calculateApp(state, action) { switch (action.type) { case ADD_ENTRY: return Object.assign({}, state, { items: [ { id: state.items.reduce((maxId, item) => Math.max(item.id, maxId), -1) + 1, expression: action.text, result: eval(action.text) }, ...state.items ] }) default: return state } }

Slide 41

Slide 41 text

import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import calculateApp from './reducers/index'; import App from './components/App'; const store = createStore(calculateApp); render( , document.getElementById('app') );

Slide 42

Slide 42 text

import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import calculateApp from './reducers/index'; import App from './components/App'; const store = createStore(calculateApp); render( , document.getElementById('app') );

Slide 43

Slide 43 text

Data Flow 1. call store.dispatch(action). 2. The Redux store calls the reducer function. 3. The Redux store saves the state tree returned by the root reducer.

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

import { connect } from 'react-redux'; import { removeEntry } from '../actions'; import EntryList from '../components/EntryList'; const mapStateToProps = (state) => { return { items: state.items } }; const PushEntryList = connect( mapStateToProps, mapDispatchToProps )(EntryList);

Slide 46

Slide 46 text

import { connect } from 'react-redux'; import { removeEntry } from '../actions'; import EntryList from '../components/EntryList'; const mapDispatchToProps = (dispatch) => { return { remove: (index) => { dispatch(removeEntry(index)); } } } const PushEntryList = connect( mapStateToProps, mapDispatchToProps )(EntryList);

Slide 47

Slide 47 text

import { connect } from 'react-redux'; import { removeEntry } from '../actions'; import EntryList from '../components/EntryList'; const mapDispatchToProps = (dispatch) => { return { remove: (index) => { dispatch(removeEntry(index)); } } } const PushEntryList = connect( mapStateToProps, mapDispatchToProps )(EntryList);

Slide 48

Slide 48 text

import { connect } from 'react-redux'; import { removeEntry } from '../actions'; import EntryList from '../components/EntryList'; const mapDispatchToProps = (dispatch) => { return { remove: (index) => { dispatch(removeEntry(index)); } } } const PushEntryList = connect( mapStateToProps, mapDispatchToProps )(EntryList);

Slide 49

Slide 49 text

render() { return (
  • {this.props.result}
    {this.props.expression}
    { PubSub.publish('entry', this.props.result); }}>Use Result { PubSub.publish('entry', this.props.expression); }}>Use Expression Remove
  • ) }

    Slide 50

    Slide 50 text

    render() { return (
  • {this.props.result}
    {this.props.expression}
    { PubSub.publish('entry', this.props.result); }}>Use Result { PubSub.publish('entry', this.props.expression); }}>Use Expression Remove
  • ) }

    Slide 51

    Slide 51 text

    /* * action types */ export const REMOVE_ENTRY = 'REMOVE_ENTRY'; /* * action creators */ export function removeEntry(index) { return { type: REMOVE_ENTRY, index }; }

    Slide 52

    Slide 52 text

    function calculateApp(state, action) { switch (action.type) { case REMOVE_ENTRY: return Object.assign({}, state, { items: [ ...state.items.slice(0, action.index), ...state.items.slice(action.index + 1) ] }) default: return state } }

    Slide 53

    Slide 53 text

    function calculateApp(state, action) { switch (action.type) { case REMOVE_ENTRY: return Object.assign({}, state, { items: [ ...state.items.slice(0, action.index), ...state.items.slice(action.index + 1) ] }) default: return state } }

    Slide 54

    Slide 54 text

    Demo Time

    Slide 55

    Slide 55 text

    MiddleWare

    Slide 56

    Slide 56 text

    Recap

    Slide 57

    Slide 57 text

    Redux Overview - State of your app is in a single store. - Only way to change the state tree is to emit an action. - specify how the actions transform the state with pure reducers.

    Slide 58

    Slide 58 text

    Do I Need Redux?

    Slide 59

    Slide 59 text

    Learn to Think in React, First.

    Slide 60

    Slide 60 text

    Redux - Helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test - Good developer experience - implement logging, hot reloading, time travel, universal apps, record and replay - Can use Redux together with React, or with any other view library.

    Slide 61

    Slide 61 text

    Resources - Getting Started with Redux
 https://egghead.io/courses/getting-started-with-redux - Building Applications with Idiomatic Redux
 https://egghead.io/courses/building-react-applications-with- idiomatic-redux

    Slide 62

    Slide 62 text

    GIthub - React Slingshot
 https://github.com/coryhouse/react-slingshot - Recalculator
 https://github.com/jonkemp/recalculator

    Slide 63

    Slide 63 text

    Thank You! @jonkemp www.scrippsnetworksinteractive.com/careers