Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Async Redux Actions With RxJS
… a love story. <3
Ben Lesh
RxJS 5 Lead
Senior Engineer
Netflix UI Platform Team
Twitter: @benlesh
GitHub: blesh
What is redux?
• Red UX – making your whole site red
• A shelter specializing in abandoned pet ducks
• SF startup for bio-...
What is redux?
• A library for managing state with reducers
• Basic functional programming wizardry
brought to the masses
...
What’s a reducer?
A simple function that takes state
and an action, and returns a new
state
What’s a reducer?
• Sometimes it returns the same state
(state, action) => state
• Sometimes it returns a new state
(state...
Reducers in Redux
const reducer = (state = { value: 0 }, action) => {
switch (action.type) {
case ‘INCREMENT’:
return { va...
Reducers are great for managing state!
An action notifies you of a change,
you return a new state! So easy!
Redux reducers handle state
transitions, but they must be
handled synchronously
But what about async?
• User interactions (mouse, keyboard, etc)
• AJAX
• Web Sockets
• Animations
• Workers, et al
A LOT of async results
can be handled synchronously
• Click a button and update a value
• Select an option and update a va...
Well, some async
is harder than others…
• AJAX cancellation
• Composed AJAX
• Debounced form submissions
• Drag and drop
•...
What do these “harder” async
stories have in common?
Composing multiple async sources.
…and cancellation!.
In redux, we use middleware to
manage async
Most redux middlewares use
callbacks or promises
Callbacks
The most primitive way to handle asynchrony in
JavaScript is with callbacks
getSomeData((data) => {
dispatch({ t...
Callback Hell
(aka “the flying V”)
getSomeData(id, (data) => {
dispatch({ type: ‘SOME_DATA’, data });
getSomeData(data.par...
Promises provide a cleaner solution
getSomeData(id)
.then(data => {
dispatch({ type: ‘SOME_DATA’, data });
return getSomeD...
Promises
• Guaranteed Future
• Immutable
• Single Value
• Caching
These two features can be problematic
in modern web appl...
Promises can’t be cancelled
This means you’re executing code
you don’t want to
Why cancellation is important
• Auto-complete
• Changing views/routes before data finishes
loading
• Tearing down resources
Loading view data without cancellation
Daredevil
Loading view data without cancellation
Daredevil
The Get Down
Since you can’t cancel the previous promise,
you’re stuck processing the response
and somehow signa...
Daredevil
A better scenario
Daredevil
The Get Down
Ideally, when we make a new request
we can abort the the old one
so it’s never handled and processe...
Thanks, Kent C Dodds!
But these days “resources” are
cheap, right?
Netflix targets devices that are 465x
slower than your laptop
Times are changing
• Laptops and desktops are very fast
• Tablets
• Smartphones
• Industrial devices
• SmartTVs
• Applianc...
Promises are a single value
Which of these are single value?
• User interactions (mouse, keyboard, etc)
• AJAX
• Web Sockets
• Animations
• Workers, e...
What do we use?
Observables
• A set of events
• Any number of values
• Over any amount of time
• Cancellable
• Lazy
RxJS
Observables and functions to create
and compose Observables
RxJS
“Lodash for async”
RxJS provides many ways to create
Observables
• interval(1000)
• fromEvent(button, ‘click’)
• from([1, 2, 3, 4]);
• of(‘he...
Subscribing to an observable
myObservable.subscribe(x => console.log(x));
Subscribing to an observable
myObservable.subscribe(
x => console.log(x),
err => console.error(err)
);
Subscribing to an observable
myObservable.subscribe(
x => console.log(x),
err => console.error(err) ,
() => console.info(‘...
Subscribing to an observable
const subscription = myObservable.subscribe(
x => console.log(x),
err => console.error(err) ,...
We have sets of events that we
can cancel!
Okay, now what?
Sets can be transformed
map, filter, reduce
Sets can be combined
concat, merge, zip
Observables are sets with
another dimension: TIME
buffer, throttle, debounce,
combineLatest
Observables are lazy so they can
be resubscribed
retry, repeat
There is an error path, so we can
catch, just like promise
myObservable.catch(err => Observable.of(‘handled’))
You can do just about anything
with RxJS and Observables!
- redux documentation
Well…
Redux has amazing tooling,
community and support around it.
Redux with middleware
provides solid architecture patterns
Async in redux without middleware
Reducer
instance state
handler kicking off async
render
Component
Async in redux with middleware
(and react-redux)
Reducer
Epic
Stateless Component
Let’s combine RxJS and redux!
redux-observable
Epic middleware for redux
What’s an “Epic”?
A function that takes a stream of all actions
dispatched, and returns a stream of actions to
dispatch.
c...
What’s an “Epic”?
“Actions in, actions out”
const pingPongEpic = (action$, store) =>
action$.ofType(‘PING’)
.map(action =>...
Basic middleware setup
import { createStore, applyMiddlware } from ‘redux’;
import { createEpicMiddleware } from ‘redux-ob...
Idiomatic redux increment decrement
with added debounce
const upEpic = (action$, store) =>
action$.ofType(‘UP’)
.debounceT...
Idiomatic redux increment decrement
with added debounce
export default const updown = (state = { value: 0 }, action) =>
{
...
Idiomatic redux increment decrement
with added debounce
WARNING: Don’t read all of this…
Auto-Complete Plain JS
Auto-Complete Epic
export const autoCompleteEpic = (action$, store) =>
action$.ofType(‘LOAD_QUERY’)
.debounceTime(500)
.sw...
compose in cancellation via dispatched
actions
export const autoCompleteEpic = (action$, store) =>
action$.ofType(‘LOAD_QU...
Multiplexed Socket Plain JS
Multiplexed Socket Epic
const socket = new WebSocketSubject('ws://stock/endpoint');
const stockTickerEpic = (action$, stor...
The Good
• Makes it very easy to compose and control
complex async tasks with RxJS and redux
• Can use redux tooling
• You...
The Bad
• Need to know redux in advance
• Should learn RxJS in advance
• RxJS has a bit of a learning curve
redux-observable
https://github.com/redux-observable/redux-observable
Co-Author Jay Phelps
Twitter: @_jayphelps
Github: jayphelps
Thank you!
@benlesh
Async Redux Actions With RxJS - React Rally 2016
Async Redux Actions With RxJS - React Rally 2016
Upcoming SlideShare
Loading in …5
×

Async Redux Actions With RxJS - React Rally 2016

5,280 views

Published on

Discusses using RxJS along with Redux via the redux-observable middleware.

Published in: Technology
  • Be the first to comment

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

×