Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplify State Management with Redux

Simplify State Management with Redux

Managing state in web applications is increasingly difficult. Redux simplifies state management by restricting how data is updated in web apps. These restrictions make it easier to manage data changes and enable powerful developer tools. In this session, we'll look at how data flows in Redux applications and the role functional programming plays using a real world application built with React and Redux.

Jonathan Kemp

November 20, 2016
Tweet

More Decks by Jonathan Kemp

Other Decks in Programming

Transcript

  1. Scripps Networks HGTV · DIY Network · Food Network ·

    Cooking Channel · Travel Channel www.scrippsnetworksinteractive.com/careers
  2. Three Principles of Redux 1. Single source of truth 2.

    State is read-only 3. Changes are made with pure functions
  3. 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.
  4. 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.
  5. Changes are made with pure functions - To specify how

    the state tree is transformed by actions, you write pure reducers.
  6. Pure Functions - Given the same input, always returns the

    same output. - Do Not Cause Side Effects. - Don’t Mutate Arguments.
  7. 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
  8. 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()) );
  9. 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()) );
  10. 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()) );
  11. /* Dispatches actions */ 
 store.dispatch() /* Listens to dispatched

    actions */ 
 store.subscribe() /* Get state from store */ 
 store.getState()
  12. 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
  13. 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
  14. { items: [ { id: 1, expression: "5 * 3",

    result: 15 }, { id: 0, expression: "5 * 2", result: 10 } ], lastResult: 15 }
  15. 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);
  16. 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);
  17. 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);
  18. 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);
  19. render() { return ( <form onSubmit={(e) => { e.preventDefault(); this.props.submit(this.input.value);

    this.input.value = ''; }}> <div className="form-group"> <input type="text" ref={node => { this.input = node }} onChange={this.handleChange} /> </div> <button type="submit">Submit</button> </form> ); }
  20. render() { return ( <form onSubmit={(e) => { e.preventDefault(); this.props.submit(this.input.value);

    this.input.value = ''; }}> <div className="form-group"> <input type="text" ref={node => { this.input = node }} onChange={this.handleChange} /> </div> <button type="submit">Submit</button> </form> ); }
  21. /* * action types */ export const ADD_ENTRY = 'ADD_ENTRY';

    /* * action creators */ export function addEntry(text) { return { type: ADD_ENTRY, text }; }
  22. 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 } }
  23. 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 } }
  24. 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( <Provider store={store}> <App /> </Provider>, document.getElementById('app') );
  25. 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( <Provider store={store}> <App /> </Provider>, document.getElementById('app') );
  26. 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.
  27. 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);
  28. 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);
  29. 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);
  30. 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);
  31. render() { return ( <li className="list-group-item"> <div>{this.props.result}</div> <div>{this.props.expression}</div> <button onClick={()

    => { PubSub.publish('entry', this.props.result); }}>Use Result</button> <button onClick={() => { PubSub.publish('entry', this.props.expression); }}>Use Expression</button> <button onClick={this.props.remove}> Remove </button> </li> ) }
  32. render() { return ( <li className="list-group-item"> <div>{this.props.result}</div> <div>{this.props.expression}</div> <button onClick={()

    => { PubSub.publish('entry', this.props.result); }}>Use Result</button> <button onClick={() => { PubSub.publish('entry', this.props.expression); }}>Use Expression</button> <button onClick={this.props.remove}> Remove </button> </li> ) }
  33. /* * action types */ export const REMOVE_ENTRY = 'REMOVE_ENTRY';

    /* * action creators */ export function removeEntry(index) { return { type: REMOVE_ENTRY, index }; }
  34. 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 } }
  35. 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 } }
  36. 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.
  37. 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.
  38. 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