Let's Redux!

Let's Redux!

For people who use React but haven’t tried Redux.
- Why - Common issues while people use React
- Redux Basic Concept

C43978727926bed201399816f7efb45d?s=128

Joseph Chiang

April 15, 2016
Tweet

Transcript

  1. Let’s Redux for people who use React but haven’t tried

    Redux @josephj6802
  2. React.js is Awesome Building a complicate UI was never so

    easy Plugins Dashboard Widget Composer ACL Term Languages
  3. Building a complicate UI was never so easy React.js is

    Awesome • Easy to learn • Manage State instead of DOM • One-way Data Flow Model • UI = fn(State) Advantages Absolutely a correct technical decision
  4. PROBLEMS!! React doesn’t We don’t have a front-end application architecture.

    “It worked well with simple components, but as our interfaces become more complicate we soon found…”
  5. Code Smells • Delegation is difficult • Top-down props •

    Mixed concerns • View logic • Data fetching • Data decoration • Poor state management • Component-specific • Mutable • Not maintainable <WidgetsEdit/> Top-level Component 1469 lines in total!!
  6. React.js = “Good” Parent class WidgetsEdit extends Component { constructor(props)

    { super(props); } componentWillMount() { $.ajax(...).then((...) => { // ... decorate data ... this.setState(...) }); } render() { let manyStates = that.state; // ... view logic ... return ( <ButtonNavList {...manyStates}> <ButtonNavItem {...manyStates}/> </ButtonNavList> <StylePreview {...manyStates}/> <CodeEditor {...manyStates}/> ) } } who takes care of everything for childs Preparing data, passing props, handling events for childs
  7. 22 Event Handlers Child passes all events to its parent

    because only the top-level component should access state Handling Children Events
  8. Delegation or Not? If your think NO, plz put inside…

    • Pass all <ButtonNavItem/> required props to <ButtonNavList/> • Handle its events in <ButtonNavList/> • Handle its events in <WidgetsEdit/> If your think YES, plz keep there… Handle child logic in top-level component. Both ways are not good… Not Convenient Dirty! Should <ButtonNavItem/> appear in top-level as illustrated?
  9. Fetching Data for Childs No Separation of Concerns

  10. Why Redux? Not have children? https://github.com/reactjs/redux/issues/151#issuecomment-173749456

  11. Why Redux? Solve all the mentioned issues • Delegation is

    difficult easier • Top-down props including events is not required • Mixed Separation of concerns • View logic • Data fetching goes to ”Action” • Data decoration goes to “Reducer” • Poor Better state management • Component App-specific • Mutable Immutable • Not Maintainable
  12. Basic Concept 1. Action 2. Reducer 3. State https://github.com/reactjs/redux/issues/151#issuecomment-134713785

  13. 1. Action Pure Object { type: 'SORT_PLUGINS', by: 'id', direction:

    'desc' } Minimum data which describes the change Only the “type” property is required
  14. 1. Action

  15. 1-1. Action Creator Pure function which creates an action function

    sortPlugins(by, direction = 'desc') { return { type: 'SORT_PLUGINS', by, direction }; } Reusable, Portable, and Easy to Test (Return promise for asynchronous action) Make it easier to create an action
  16. 2. Reducer Pure function which returns the next state function

    reducer(state = {}, action) { switch (action.type) { case 'SORT_PLUGINS': return { ...state, plugins: { orderBy: action.by, orderByDirection: action.direction, data: _.sortByOrder(state.data, action.by, 
 action.direction) } }; default: return state; }; } reducer(previousState, action) => state Initial State Always return current state
  17. 2. Reducer reducer(prevState, action) => nextState

  18. 3. Store A plain object which holds application state {

    plugins: { data: [ {id: 1, name: 'AdRoll'}, {id: 2, name: 'Agile CRM'}, {id: 3, name: 'Brand Networks'} ], orderBy: 'id', orderByDirection: 'desc' } } Dispatching actions is the only way to update store Store is the only one state for the whole app
  19. 3. Store

  20. 3-1. Store Creation with Reducer (the spec of your store)

    store = createStore(reducer); import {createStore} from 'redux'; let store = createStore(reducer); The store provides several useful API methods
  21. store.dispatch(sortPlugins('name', 'desc')); 3-2. Store APIs store#dispatch store#subscribe let currentValue; store.subscribe(()

    => { let previousValue = currentValue; currentValue = store.getState(); if (previousValue === currentValue) { return; } // DO SOMETHING... }); store#getState
  22. Redux Data Flow Reducer Store Action Action Action store =

    createStore(reducerFn); store.subscribe(eventHandlerFn); App store.dispatch(actionCreatorFn); reducerFn = (currentState, action) => nextState
  23. Redux Data Flow Reducer Store Action Action Action store =

    createStore(reducerFn); store.subscribe(eventHandlerFn); App store.dispatch(actionCreatorFn); reducerFn = (currentState, action) => nextState That’s it! • Super easy for both code and concept • Pure functions & objects w/o side-effects • Provides a better data flow for JS apps • Not just for React.js apps • jQuery or Node.js? No problem!
  24. Redux and React? First, let’s check the list again •

    Delegation is easier • Top-down props is not required • Separation of concerns • View logic • Data fetching goes to ”Action” • Data decoration goes to “Reducer” • Better state management • App-specific • Immutable • Maintainable • Delegation is easier • Top-down props is not required
  25. React.js = “Good” Parent class WidgetsEdit extends Component { constructor(props)

    { super(props); } componentWillMount() { $.ajax(...).then((...) => { // ... decorate data ... this.setState(...) }); } render() { let manyStates = that.state; // ... decorate state ... return ( <ButtonNavList {...manyStates}> <ButtonNavItem {...manyStates}/> </ButtonNavList> <StylePreview {...manyStates}/> <CodeEditor {...manyStates}/> ) } } who takes care of everything of its child components Preparing data & handling events for child components
  26. React + Redux = Bad Parent import React, {Component} from

    'react'; import {connect} from 'react-redux'; import {fetchData} from ‘./actions'; class WidgetsEdit extends Component { constructor(props) { super(props); } componentWillMount() { this.props.dispatch(fetchData()); } render() { return ( <ButtonNavList> <ButtonNavItem/> <ButtonNavList> <StylePreview/> <CodeEditor/> ) } } WidgetsEdit = connect(mapStateToProps)(WidgetsEdit); who only cares about himself Delegation = Preparing data and dispatching for itself
  27. React.js w/ Redux Child component connects to store by itself

    import React from 'react'; import {connect} from 'react-redux'; import {modeSwitch} from './actions'; let StylePreview({url, dispatch} = props) => { return ( <a onClick={() => dispatch(modeSwitch('mobile'))}>/> Mobile View </a> <iframe src={url}/> ); } StylePreview = connect((state) => { // map state to props return { url: state.widget.url, } })(StylePreview); … And dispatches actions by itself
  28. Summary • Good start to learn functional programming • Add

    complexity but also add maintenance = worthy • Single Store • Time Travel (e.g. Undo) is possible • No need to access React state API anymore • Not only for React.js
  29. Further Reading https://egghead.io/series/getting-started-with-redux egghead.io Very good material for learning Redux!

  30. Discussion - Store Structure One Store - Could it possibly

    fit our application?
  31. THANK YOU! Let’s Redux!!