Successfully reported this slideshow.
Your SlideShare is downloading. ×

Async Redux Actions With RxJS - React Rally 2016

Ad

Async Redux Actions With RxJS
… a love story. <3

Ad

Ben Lesh
RxJS 5 Lead
Senior Engineer
Netflix UI Platform Team
Twitter: @benlesh
GitHub: blesh

Ad

What is redux?
• Red UX – making your whole site red
• A shelter specializing in abandoned pet ducks
• SF startup for bio-...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Upcoming SlideShare
Rethink Async With RXJS
Rethink Async With RXJS
Loading in …3
×

Check these out next

1 of 73 Ad
1 of 73 Ad

More Related Content

Async Redux Actions With RxJS - React Rally 2016

  1. 1. Async Redux Actions With RxJS … a love story. <3
  2. 2. Ben Lesh RxJS 5 Lead Senior Engineer Netflix UI Platform Team Twitter: @benlesh GitHub: blesh
  3. 3. What is redux? • Red UX – making your whole site red • A shelter specializing in abandoned pet ducks • SF startup for bio-engineered foie gras • An evil recycling program for unwanted ducks
  4. 4. What is redux? • A library for managing state with reducers • Basic functional programming wizardry brought to the masses • A brilliant piece of work by Dan Abramov (and many others)
  5. 5. What’s a reducer? A simple function that takes state and an action, and returns a new state
  6. 6. What’s a reducer? • Sometimes it returns the same state (state, action) => state • Sometimes it returns a new state (state, action) => state + action.value
  7. 7. Reducers in Redux const reducer = (state = { value: 0 }, action) => { switch (action.type) { case ‘INCREMENT’: return { value: state.value + 1 }; case ‘DECREMENT’: return { value: state.value – 1 }; default: return state; } };
  8. 8. Reducers are great for managing state! An action notifies you of a change, you return a new state! So easy!
  9. 9. Redux reducers handle state transitions, but they must be handled synchronously
  10. 10. But what about async? • User interactions (mouse, keyboard, etc) • AJAX • Web Sockets • Animations • Workers, et al
  11. 11. A LOT of async results can be handled synchronously • Click a button and update a value • Select an option and update a value • Get a single AJAX request and update view • Mouse moves updating some coordinates
  12. 12. Well, some async is harder than others… • AJAX cancellation • Composed AJAX • Debounced form submissions • Drag and drop • Advanced web socket use
  13. 13. What do these “harder” async stories have in common? Composing multiple async sources. …and cancellation!.
  14. 14. In redux, we use middleware to manage async
  15. 15. Most redux middlewares use callbacks or promises
  16. 16. Callbacks The most primitive way to handle asynchrony in JavaScript is with callbacks getSomeData((data) => { dispatch({ type: ‘I_HAVE_DATA’, data }); });
  17. 17. Callback Hell (aka “the flying V”) getSomeData(id, (data) => { dispatch({ type: ‘SOME_DATA’, data }); getSomeData(data.parentId, (parent) => { dispatch({ type: ‘MORE_DATA’, data }); getSomeData(parent.parentId, (grandparent) => { dispatch({ type: ‘DATAX3_YOLO_LOL’, data }); }); }); });
  18. 18. Promises provide a cleaner solution getSomeData(id) .then(data => { dispatch({ type: ‘SOME_DATA’, data }); return getSomeData(data.parentId); }) .then(data => { dispatch({ type: ‘MORE_DATA’, data }); return getSomeData(data.parentId); }) .then(data => { dispatch({ type: ‘DATAX3_YOLO_LOL’, data }); })
  19. 19. Promises • Guaranteed Future • Immutable • Single Value • Caching These two features can be problematic in modern web applications
  20. 20. Promises can’t be cancelled This means you’re executing code you don’t want to
  21. 21. Why cancellation is important • Auto-complete • Changing views/routes before data finishes loading • Tearing down resources
  22. 22. Loading view data without cancellation
  23. 23. Daredevil Loading view data without cancellation
  24. 24. Daredevil The Get Down Since you can’t cancel the previous promise, you’re stuck processing the response and somehow signaling “disinterest” Loading view data without cancellation Here’s Daredevil!
  25. 25. Daredevil A better scenario
  26. 26. Daredevil The Get Down Ideally, when we make a new request we can abort the the old one so it’s never handled and processed A better scenario
  27. 27. Thanks, Kent C Dodds!
  28. 28. But these days “resources” are cheap, right? Netflix targets devices that are 465x slower than your laptop
  29. 29. Times are changing • Laptops and desktops are very fast • Tablets • Smartphones • Industrial devices • SmartTVs • Appliances • Wearables
  30. 30. Promises are a single value
  31. 31. Which of these are single value? • User interactions (mouse, keyboard, etc) • AJAX • Web Sockets • Animations • Workers, et al
  32. 32. What do we use?
  33. 33. Observables • A set of events • Any number of values • Over any amount of time • Cancellable • Lazy
  34. 34. RxJS Observables and functions to create and compose Observables
  35. 35. RxJS “Lodash for async”
  36. 36. RxJS provides many ways to create Observables • interval(1000) • fromEvent(button, ‘click’) • from([1, 2, 3, 4]); • of(‘hello’); • ajax.getJSON(‘http://example.com’); • webSocket(‘ws://echo.websocket.com’); • many, many more…
  37. 37. Subscribing to an observable myObservable.subscribe(x => console.log(x));
  38. 38. Subscribing to an observable myObservable.subscribe( x => console.log(x), err => console.error(err) );
  39. 39. Subscribing to an observable myObservable.subscribe( x => console.log(x), err => console.error(err) , () => console.info(‘complete’) );
  40. 40. Subscribing to an observable const subscription = myObservable.subscribe( x => console.log(x), err => console.error(err) , () => console.info(‘complete’) ); subscription.unsubscribe();
  41. 41. We have sets of events that we can cancel! Okay, now what?
  42. 42. Sets can be transformed map, filter, reduce
  43. 43. Sets can be combined concat, merge, zip
  44. 44. Observables are sets with another dimension: TIME buffer, throttle, debounce, combineLatest
  45. 45. Observables are lazy so they can be resubscribed retry, repeat
  46. 46. There is an error path, so we can catch, just like promise myObservable.catch(err => Observable.of(‘handled’))
  47. 47. You can do just about anything with RxJS and Observables!
  48. 48. - redux documentation
  49. 49. Well… Redux has amazing tooling, community and support around it.
  50. 50. Redux with middleware provides solid architecture patterns
  51. 51. Async in redux without middleware Reducer instance state handler kicking off async render Component
  52. 52. Async in redux with middleware (and react-redux) Reducer Epic Stateless Component
  53. 53. Let’s combine RxJS and redux!
  54. 54. redux-observable Epic middleware for redux
  55. 55. What’s an “Epic”? A function that takes a stream of all actions dispatched, and returns a stream of actions to dispatch. const pingPongEpic = (action$, store) => action$.ofType(‘PING’) .map(action => ({ type: ‘PONG’ });
  56. 56. What’s an “Epic”? “Actions in, actions out” const pingPongEpic = (action$, store) => action$.ofType(‘PING’) .map(action => ({ type: ‘PONG’ });
  57. 57. Basic middleware setup import { createStore, applyMiddlware } from ‘redux’; import { createEpicMiddleware } from ‘redux-observable’; import reducerFn, { epicFn } from ‘./redux/updown’; const epicMiddleware = createEpicMiddleware(epicFn); const store = createStore( reducerFn, applyMiddleware(epicMiddleware) );
  58. 58. Idiomatic redux increment decrement with added debounce const upEpic = (action$, store) => action$.ofType(‘UP’) .debounceTime(1000) .map(() => ({ type: ‘INCREMENT’ })); const downEpic = (action$, store) => action$.ofType(‘DOWN’) .debounceTime(1000) .map(() => ({ type: ‘DECREMENT’ })); export const updownEpic = combineEpics(upEpic, downEpic);
  59. 59. Idiomatic redux increment decrement with added debounce export default const updown = (state = { value: 0 }, action) => { switch (action.type) { case ‘INCREMENT’: return { value: state.value + 1 }; case ‘DECREMENT’: return { value: state.value – 1 }; default: return state; } };
  60. 60. Idiomatic redux increment decrement with added debounce
  61. 61. WARNING: Don’t read all of this…
  62. 62. Auto-Complete Plain JS
  63. 63. Auto-Complete Epic export const autoCompleteEpic = (action$, store) => action$.ofType(‘LOAD_QUERY’) .debounceTime(500) .switchMap(action => ajax.getJSON(`http://endpoint/q=${action.value}`) .map(results => ({ type: ‘QUERY_RESULTS’, results })) );
  64. 64. compose in cancellation via dispatched actions export const autoCompleteEpic = (action$, store) => action$.ofType(‘LOAD_QUERY’) .debounceTime(500) .switchMap(action => ajax.getJSON(`http://endpoint/q=${action.value}`) .map(results => ({ type: ‘QUERY_RESULTS’, results })) .takeUntil(action$.ofType(‘CANCEL_QUERY’)) );
  65. 65. Multiplexed Socket Plain JS
  66. 66. Multiplexed Socket Epic const socket = new WebSocketSubject('ws://stock/endpoint'); const stockTickerEpic = (action$, store) => action$.ofType('GET_TICKER_STREAM') .mergeMap(action => socket.multiplex( () => ({ sub: action.ticker }), () => ({ unsub: action.ticker }), msg => msg.ticker === action.ticker ) .retryWhen( err => window.navigator.onLine ? Observable.timer(1000) : Observable.fromEvent(window, 'online') ) .takeUntil( action$.ofType('CLOSE_TICKER_STREAM') .filter(closeAction => closeAction.ticker === action.ticker) ) .map(tick => ({ type: 'TICKER_TICK', tick })) );
  67. 67. The Good • Makes it very easy to compose and control complex async tasks with RxJS and redux • Can use redux tooling • You don’t end up managing your own Rx subscriptions • If used with react-redux, makes all of your components stateless
  68. 68. The Bad • Need to know redux in advance • Should learn RxJS in advance • RxJS has a bit of a learning curve
  69. 69. redux-observable https://github.com/redux-observable/redux-observable
  70. 70. Co-Author Jay Phelps Twitter: @_jayphelps Github: jayphelps
  71. 71. Thank you! @benlesh

Editor's Notes

  • Remove mutation?
  • Have a redux tie-in (dispatch instead of doStuff)

    note threading of errors
  • Note promises are only 1 event, this is 0-N.

×