SlideShare une entreprise Scribd logo
1  sur  98
Télécharger pour lire hors ligne
RxJS + Redux + React = Amazing
Jay Phelps | @_jayphelps
Side Effect Management with RxJS
Jay Phelps | @_jayphelps
Managing state stuff is hard
Jay Phelps | @_jayphelps
Redux makes it simple
(not necessarily easy)
Jay Phelps | @_jayphelps
Managing async stuff is harder
Jay Phelps | @_jayphelps
Some async is complex regardless
of the abstraction
Jay Phelps | @_jayphelps
RxJS makes it manageable
Jay Phelps
Senior Software Engineer |
@_jayphelps
What is redux?
Jay Phelps | @_jayphelps
Crash Course
Jay Phelps | @_jayphelps
What is redux?
Jay Phelps | @_jayphelps
Provides predicable state management using
actions and reducers
What's an "action"?
Jay Phelps | @_jayphelps
Describes something has (or should) happen, but
they don't specify how it should be done
Jay Phelps | @_jayphelps
{	
		type:	'CREATE_TODO',	
		payload:	'Build	my	first	Redux	app'	
}
What's an "reducer"?
Jay Phelps | @_jayphelps
A pure function that takes the previous state
and an action and returns the new state
What's an "reducer"?
Jay Phelps | @_jayphelps
Sometimes it returns the previous state
(state,	action)	=>	state
What's an "reducer"?
Jay Phelps | @_jayphelps
Sometimes it computes new state
(state,	action)	=>	state	+	action.payload
Jay Phelps | @_jayphelps
const	counter	=	(state	=	0,	action)	=>	{	
		switch	(action.type)	{	
				case	'INCREMENT':	
						return	state	+	1;	
				case	'DECREMENT':	
						return	state	-	1;	
						
				default:	
						return	state;	
		}	
};
Jay Phelps | @_jayphelps
Reducers handle state transitions, but they
must be done synchronously.
Jay Phelps | @_jayphelps
const	counter	=	(state	=	0,	action)	=>	{	
		switch	(action.type)	{	
				case	'INCREMENT':	
						return	state	+	1;	
				case	'DECREMENT':	
						return	state	-	1;	
						
				default:	
						return	state;	
		}	
};
Jay Phelps | @_jayphelps
What are async stuff do we commonly do?
Async
Jay Phelps | @_jayphelps
• User interactions (mouse, keyboard, etc)
• AJAX
• Timers/Animations
• Web Sockets
• Work Workers, etc
Jay Phelps | @_jayphelps
Some can be handled synchronously
Jay Phelps | @_jayphelps
<button	onClick={()	=>	dispatch({	type:	'INCREMENT'	})}>	
		Increment	
</button>
Jay Phelps | @_jayphelps
const	counter	=	(state	=	0,	action)	=>	{	
		switch	(action.type)	{	
				case	'INCREMENT':	
						return	state	+	1;	
				case	'DECREMENT':	
						return	state	-	1;	
						
				default:	
						return	state;	
		}	
};
Jay Phelps | @_jayphelps
Sometimes you need more control
Jay Phelps | @_jayphelps
• AJAX cancellation/composing
• Debounce/throttle/buffer/etc
• Drag and Drop
• Web Sockets, Work Workers, etc
Jay Phelps | @_jayphelps
Use middleware to manage async / side effects
Jay Phelps | @_jayphelps
Most of them use callbacks or Promises
Callbacks
Jay Phelps | @_jayphelps
The most primitive way to handle async in JavaScript
Callbacks
Jay Phelps | @_jayphelps
fetchSomeData((error,	data)	=>	{	
		if	(!error)	{	
				dispatch({	type:	'HERES_THE_DATA',	data	});	
		}	
});
Callback Hell
Jay Phelps | @_jayphelps
fetchSomeData(id,	(error,	data)	=>	{	
		if	(!error)	{	
				dispatch({	type:	'HERES_THE_FIRST_CALL_DATA',	data	});	
				fetchSomeData(data.parentId,	(error,	data)	=>	{	
						if	(!error)	{	
								dispatch({	type:	'HERES_SOME_MORE',	data	});	
									
								fetchSomeData(data.parentId,	(error,	data)	=>	{	
										if	(!error)	{	
												dispatch({	type:	'OMG_MAKE_IT_STOP',	data	});	
										}	
								});	
						}	
				});	
		}	
});
Promises
Jay Phelps | @_jayphelps
fetchSomeData(id)	
		.then(data	=>	{	
				dispatch({	type:	'HERES_THE_FIRST_CALL_DATA',	data	});	
				return	fetchSomeData(data.parentId);	
		})	
		.then(data	=>	{	
				dispatch({	type:	'HERES_SOME_MORE',	data	});	
				return	fetchSomeData(data.parentId);	
		})	
		.then(data	=>	{	
				dispatch({	type:	'OKAY_IM_DONE',	data	});	
		});
Promises
Jay Phelps | @_jayphelps
• Guaranteed future
• Immutable
• Single value
• Caching
Promises
Jay Phelps | @_jayphelps
• Guaranteed future
• Immutable
• Single value
• Caching
Promises
Jay Phelps | @_jayphelps
• Guaranteed future
• Immutable
• Single value
• Caching
Jay Phelps | @_jayphelps
Promises cannot be cancelled
Jay Phelps | @_jayphelps
Why would you want to cancel?
Jay Phelps | @_jayphelps
• Changing routes/views
• Auto-complete
• User wants you to
Jay Phelps | @_jayphelps
• Changing routes/views
• Auto-complete
• User wants you to
Jay Phelps | @_jayphelps
Daredevil
Jay Phelps | @_jayphelps
Daredevil	
The	Get	Down	
Here’s	Daredevil!
Jay Phelps | @_jayphelps
Daredevil
Jay Phelps | @_jayphelps
Daredevil	
The	Get	Down
Jay Phelps | @_jayphelps
Cancelling is common and often overlooked
Promises
Jay Phelps | @_jayphelps
• Guaranteed future
• Immutable
• Single value
• Caching
Only AJAX is single value
Jay Phelps | @_jayphelps
• User interactions (mouse, keyboard, etc
• AJAX
• Animations
• WebSockets, Workers, etc
Jay Phelps | @_jayphelps
What do we use?
Jay Phelps | @_jayphelps
Observables
Observables
Jay Phelps | @_jayphelps
• Stream of zero, one, or more values
• Over any amount of time
• Cancellable
Jay Phelps | @_jayphelps
Streams are a set, with a dimension of time
Jay Phelps | @_jayphelps
Being standardized for ECMAScript aka JavaScript
RxJS
Jay Phelps | @_jayphelps
“lodash for async” - Ben Lesh
Crash Course
Jay Phelps | @_jayphelps
Creating Observables
Jay Phelps | @_jayphelps
• of('hello')	
• from([1,	2,	3,	4])		
• interval(1000)	
• ajax('http://example.com')	
• webSocket('ws://echo.websocket.com')	
• fromEvent(button,	‘click')	
• many more…
Subscribing
Jay Phelps | @_jayphelps
myObservable.subscribe(	
		value	=>	console.log('next',	value)	
);
Subscribing
Jay Phelps | @_jayphelps
myObservable.subscribe(	
		value	=>	console.log('next',	value),	
		err	=>	console.error('error',	err)	
);
Subscribing
Jay Phelps | @_jayphelps
myObservable.subscribe(	
		value	=>	console.log('next',	value),	
		err	=>	console.error('error',	err),	
		()	=>	console.info('complete!')	
);
Observables can be transformed
Jay Phelps | @_jayphelps
map, filter, reduce
Observables can be combined
Jay Phelps | @_jayphelps
concat, merge, zip
Observables represent time
Jay Phelps | @_jayphelps
debounce, throttle, buffer, combineLatest
Observables are lazy
Jay Phelps | @_jayphelps
retry, repeat
Jay Phelps | @_jayphelps
Observables can represent just about anything
Jay Phelps | @_jayphelps
Let’s combine RxJS and Redux!
Jay Phelps | @_jayphelps
RxJS + Redux + React = Amazing
Side effect management for redux, using Epics
What is an Epic?
Jay Phelps | @_jayphelps
A function that takes a stream of all actions dispatched
and returns a stream of new actions to dispatch
Jay Phelps | @_jayphelps
“actions in, actions out”
//	This	is	pseudo	code,	not	real	
function	pingPong(action,	store)	{	
		if	(action.type	===	'PING')	{	
				return	{	type:	'PONG'	};	
		}	
}
Jay Phelps | @_jayphelps
Sort of like this
function	pingPongEpic(action$,	store)	{	
		return	action$.ofType('PING')	
				.map(action	=>	({	type:	'PONG'	}));	
}
An Epic
Jay Phelps | @_jayphelps
const	pingPongEpic	=	(action$,	store)	=>	
		action$.ofType('PING')	
				.map(action	=>	({	type:	'PONG'	}));
An Epic
Jay Phelps | @_jayphelps
An Epic
Jay Phelps | @_jayphelps
const	pingPongEpic	=	(action$,	store)	=>	
		action$.ofType('PING')	
				.delay(1000)	//	<—	that's	it	
				.map(action	=>	({	type:	'PONG'	}));
Jay Phelps | @_jayphelps
const	isPinging	=	(state	=	false,	action)	=>	{	
		switch	(action.type)	{	
				case	'PING':	
						return	true;	
				case	'PONG':	
						return	false;	
				default:	
						return	state;	
		}	
};
const	pingPongEpic	=	(action$,	store)	=>	
		action$.ofType('PING')	
				.delay(1000)	
				.map(action	=>	({	type:	'PONG'	}));
Jay Phelps | @_jayphelps
Jay Phelps | @_jayphelps
Debounced increment / decrement button
Jay Phelps | @_jayphelps
const	counter	=	(state	=	0,	action)	=>	{	
		switch	(action.type)	{	
				case	'INCREMENT':	
						return	state	+	1;	
				case	'DECREMENT':	
						return	state	-	1;	
						
				default:	
						return	state;	
		}	
};
Jay Phelps | @_jayphelps
const	incrementEpic	=	(action$,	store)	=>	
		action$.ofType('INCREMENT_DEBOUNCED')	
				.debounceTime(1000)	
				.map(()	=>	({	type:	'INCREMENT'	}));	
const	decrementEpic	=	(action$,	store)	=>	
		action$.ofType('DECREMENT_DEBOUNCED')	
				.debounceTime(1000)	
				.map(()	=>	({	type:	'DECREMENT'	}));
Jay Phelps | @_jayphelps
const	incrementEpic	=	(action$,	store)	=>	
		action$.ofType('INCREMENT_DEBOUNCED')	
				.debounceTime(1000)	
				.map(()	=>	({	type:	'INCREMENT'	}));	
const	decrementEpic	=	(action$,	store)	=>	
		action$.ofType('DECREMENT_DEBOUNCED')	
				.debounceTime(1000)	
				.map(()	=>	({	type:	'DECREMENT'	}));
Jay Phelps | @_jayphelps
Those are contrived examples, obviously
Jay Phelps | @_jayphelps
Warning: non-trivial examples ahead,
don’t struggle to read them entirely
Jay Phelps | @_jayphelps
Auto-complete
Jay Phelps | @_jayphelps
		onKeyUp(e)	{	
				const	{	store	}	=	this.props;	
				const	{	value	}	=	e.target.value;	
				if	(this.queryId)	{	
						clearTimeout(this.queryId);	
				}	
				this.queryId	=	setTimeout(()	=>	{	
						if	(this.xhr)	{	
								this.xhr.abort();	
						}	
						const	xhr	=	this.xhr	=	new	XMLHttpRequest();	
						xhr.open('GET',	'https://api.github.com/search/users?q='	+	value);	
						xhr.onload	=	()	=>	{	
								if	(xhr.status	===	200)	{	
										store.dispatch({	
												type:	'QUERY_FULFILLED',	
												payload:	JSON.parse(xhr.response).items	
										});	
								}	else	{	
										store.dispatch({	
												type:	'QUERY_REJECTED',	
												error:	true,	
												payload:	{	
														message:	xhr.response,	
														status:	xhr.status	
												}	
										});	
								}	
						};	
						xhr.send();	
				},	500);	
		}
Plain JS
Jay Phelps | @_jayphelps
Epic
const	autoCompleteEpic	=	(action$,	store)	=>	
		action$.ofType('QUERY')	
				.debounceTime(500)	
				.switchMap(action	=>	
						ajax('https://api.github.com/search/users?q='	+	value)	
								.map(payload	=>	({	
										type:	'QUERY_FULFILLED',	
										payload	
								}))	
				);
Jay Phelps | @_jayphelps
Epic
const	autoCompleteEpic	=	(action$,	store)	=>	
		action$.ofType('QUERY')	
				.debounceTime(500)	
				.switchMap(action	=>	
						ajax('https://api.github.com/search/users?q='	+	value)	
								.map(payload	=>	({	
										type:	'QUERY_FULFILLED',	
										payload	
								}))	
								.catch(payload	=>	[{	
										type:	'QUERY_REJECTED',	
										error:	true,	
										payload	
								}])	
				);
Jay Phelps | @_jayphelps
Epic
const	autoCompleteEpic	=	(action$,	store)	=>	
		action$.ofType('QUERY')	
				.debounceTime(500)	
				.switchMap(action	=>	
						ajax('https://api.github.com/search/users?q='	+	value)	
								.map(payload	=>	({	
										type:	'QUERY_FULFILLED',	
										payload	
								}))	
								.takeUntil(action$.ofType('CANCEL_QUERY'))	
								.catch(payload	=>	[{	
										type:	'QUERY_REJECTED',	
										error:	true,	
										payload	
								}])	
				);
Jay Phelps | @_jayphelps
OK, show me really non-trivial examples
Jay Phelps | @_jayphelps
Bidirectional, multiplexed Web Sockets
Jay Phelps | @_jayphelps
class	Example	{	
		@autobind	
		checkChange(e)	{	
				const	{	value:	key,	checked	}	=	e.target;	
				this.subs	=	this.subs	||	[];	
				if	(checked)	{	
						const	handler	=	e	=>	{	
								const	data	=	JSON.parse(e.data);	
								if	(data.key	===	key)	{	
										this.updateValue(key,	data.value);	
								}	
						};	
						this.subs.push({	key,	handler	})	
						const	socket	=	this.getSocket(()	=>	{	
								this.setState({	
										socketOpen:	true	
								});	
								this.subs.forEach(({	key	})	=>	socket.send(JSON.stringify({	type:	'sub',	key})));	
						});	
						socket.addEventListener('message',	handler);	
				}	else	{	
						const	index	=	this.subs.findIndex(x	=>	x.key	===	key);	
						if	(index	!==	-1)	{	
								this.subs.splice(index,	1);	
						}	
						const	{	socket	}	=	this;	
						if	(socket	&&	socket.readyState	===	1)	{	
								socket.send(JSON.stringify({	type:	'unsub',	key	}));	
								this.setInactive(key)l	
								if	(this.subs.length	===	0)	{	
										socket.close();	
								}	
						}	
				}	
		}	
		componentWillUnMount()	{	
				if	(this.socket	&&	this.socket.readyState	===	1)	{	
						this.socket.close();	
				}	
		}
Plain JS
		getSocket(callback)	{	
				const	{	socket	}	=	this;	
				if	(socket	&&	socket.readyState	===	1)	{	
						setTimeot(callback);	
				}	else	{	
						if	(this.reconnectId)	{	
								clearTimeout(this.reconnectId);	
						}	
						socket	=	this.socket	=	new	WebSocket(‘ws://localhost:3000');	
						socket.onopen	=	()	=>	{	
								callback();	
						};	
						socket.onerror	=	()	=>	{	
								this.reconnectId	=	setTimeout(()	=>	this.getSocket(callback),	1000);	
								this.setState({	socketOpen:	false	});	
						};	
						socket.onclose	=	(e)	=>	{	
								if	(!e.wasClean)	{	
										this.reconnectId	=	setTimeout(()	=>	this.getSocket(callback),	1000);	
								}	
								this.setState({	socketOpen:	false	});	
						};	
				}	
					
				return	socket;	
		}	
}
Jay Phelps | @_jayphelps
Too much code
Jay Phelps | @_jayphelps
As an Epic
const	socket	=	WebSocketSubject.create('ws://stock/endpoint');	
const	stockTickerEpic	=	(action$,	store)	=>	
		action$.ofType('START_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	}))	
				);
redux-observable
Jay Phelps | @_jayphelps
• Makes it easier to compose and control
complex async tasks, over any amount of time
• You don't need to manage your own Rx
subscriptions
• You can use redux tooling
But…
Jay Phelps | @_jayphelps
Jay Phelps | @_jayphelps
You should probably know redux and RxJS in advance
Jay Phelps | @_jayphelps
RxJS has a bit of a learning curve
Jay Phelps | @_jayphelps
“Reactive Programming”
Co-author
Jay Phelps | @_jayphelps
Ben Lesh
Senior UI Engineer |
@benlesh
Jay Phelps | @_jayphelps
https://redux-observable.js.org
RxJS + Redux + React = Amazing
Thanks!
@_jayphelps

Contenu connexe

Tendances

State management in react applications (Statecharts)
State management in react applications (Statecharts)State management in react applications (Statecharts)
State management in react applications (Statecharts)Tomáš Drenčák
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooksSamundra khatri
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux IntroductionNikolaus Graf
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners Varun Raj
 
Redux Saga - Under the hood
Redux Saga - Under the hoodRedux Saga - Under the hood
Redux Saga - Under the hoodWaqqas Jabbar
 
React.js and Redux overview
React.js and Redux overviewReact.js and Redux overview
React.js and Redux overviewAlex Bachuk
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to HooksSoluto
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJSBrainhub
 
Basics of React Hooks.pptx.pdf
Basics of React Hooks.pptx.pdfBasics of React Hooks.pptx.pdf
Basics of React Hooks.pptx.pdfKnoldus Inc.
 

Tendances (20)

State management in react applications (Statecharts)
State management in react applications (Statecharts)State management in react applications (Statecharts)
State management in react applications (Statecharts)
 
Understanding react hooks
Understanding react hooksUnderstanding react hooks
Understanding react hooks
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux Introduction
 
react redux.pdf
react redux.pdfreact redux.pdf
react redux.pdf
 
React workshop
React workshopReact workshop
React workshop
 
Ngrx slides
Ngrx slidesNgrx slides
Ngrx slides
 
React hooks
React hooksReact hooks
React hooks
 
React js
React jsReact js
React js
 
React js for beginners
React js for beginnersReact js for beginners
React js for beginners
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
 
React with Redux
React with ReduxReact with Redux
React with Redux
 
Redux Saga - Under the hood
Redux Saga - Under the hoodRedux Saga - Under the hood
Redux Saga - Under the hood
 
React.js and Redux overview
React.js and Redux overviewReact.js and Redux overview
React.js and Redux overview
 
React/Redux
React/ReduxReact/Redux
React/Redux
 
React new features and intro to Hooks
React new features and intro to HooksReact new features and intro to Hooks
React new features and intro to Hooks
 
React-cours.pdf
React-cours.pdfReact-cours.pdf
React-cours.pdf
 
Introduction to RxJS
Introduction to RxJSIntroduction to RxJS
Introduction to RxJS
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
Basics of React Hooks.pptx.pdf
Basics of React Hooks.pptx.pdfBasics of React Hooks.pptx.pdf
Basics of React Hooks.pptx.pdf
 
Context API in React
Context API in ReactContext API in React
Context API in React
 

Similaire à RxJS + Redux + React = Amazing

Real-time Insights, powered by Reactive Programming
Real-time Insights, powered by Reactive ProgrammingReal-time Insights, powered by Reactive Programming
Real-time Insights, powered by Reactive ProgrammingJay Phelps
 
React, Powered by WebAssembly
React, Powered by WebAssemblyReact, Powered by WebAssembly
React, Powered by WebAssemblyJay Phelps
 
The WebAssembly Revolution Has Begun
The WebAssembly Revolution Has BegunThe WebAssembly Revolution Has Begun
The WebAssembly Revolution Has BegunJay Phelps
 
Backpressure? Resistance is not futile. (Uphill Conf 2019)
Backpressure? Resistance is not futile. (Uphill Conf 2019)Backpressure? Resistance is not futile. (Uphill Conf 2019)
Backpressure? Resistance is not futile. (Uphill Conf 2019)Jay Phelps
 
WebAssembly Demystified
WebAssembly DemystifiedWebAssembly Demystified
WebAssembly DemystifiedJay Phelps
 
Asynchronous Interfaces
Asynchronous InterfacesAsynchronous Interfaces
Asynchronous Interfacesmaccman
 
WebAssembly. Neither Web Nor Assembly, All Revolutionary
WebAssembly. Neither Web Nor Assembly, All RevolutionaryWebAssembly. Neither Web Nor Assembly, All Revolutionary
WebAssembly. Neither Web Nor Assembly, All RevolutionaryC4Media
 
Intro to php
Intro to phpIntro to php
Intro to phpSp Singh
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Entry-level PHP for WordPress
Entry-level PHP for WordPressEntry-level PHP for WordPress
Entry-level PHP for WordPresssprclldr
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQueryhowlowck
 
Effectively Testing Services - Burlington Ruby Conf
Effectively Testing Services - Burlington Ruby ConfEffectively Testing Services - Burlington Ruby Conf
Effectively Testing Services - Burlington Ruby Confneal_kemp
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptGuy Royse
 
P H P Part I I, By Kian
P H P  Part  I I,  By  KianP H P  Part  I I,  By  Kian
P H P Part I I, By Kianphelios
 
Test driven APIs with Laravel
Test driven APIs with LaravelTest driven APIs with Laravel
Test driven APIs with LaravelMichael Peacock
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applicationselliando dias
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryRemy Sharp
 
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...Rencore
 

Similaire à RxJS + Redux + React = Amazing (20)

Real-time Insights, powered by Reactive Programming
Real-time Insights, powered by Reactive ProgrammingReal-time Insights, powered by Reactive Programming
Real-time Insights, powered by Reactive Programming
 
React, Powered by WebAssembly
React, Powered by WebAssemblyReact, Powered by WebAssembly
React, Powered by WebAssembly
 
The WebAssembly Revolution Has Begun
The WebAssembly Revolution Has BegunThe WebAssembly Revolution Has Begun
The WebAssembly Revolution Has Begun
 
Backpressure? Resistance is not futile. (Uphill Conf 2019)
Backpressure? Resistance is not futile. (Uphill Conf 2019)Backpressure? Resistance is not futile. (Uphill Conf 2019)
Backpressure? Resistance is not futile. (Uphill Conf 2019)
 
WebAssembly Demystified
WebAssembly DemystifiedWebAssembly Demystified
WebAssembly Demystified
 
Asynchronous Interfaces
Asynchronous InterfacesAsynchronous Interfaces
Asynchronous Interfaces
 
WebAssembly. Neither Web Nor Assembly, All Revolutionary
WebAssembly. Neither Web Nor Assembly, All RevolutionaryWebAssembly. Neither Web Nor Assembly, All Revolutionary
WebAssembly. Neither Web Nor Assembly, All Revolutionary
 
Intro to php
Intro to phpIntro to php
Intro to php
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Entry-level PHP for WordPress
Entry-level PHP for WordPressEntry-level PHP for WordPress
Entry-level PHP for WordPress
 
Ditching JQuery
Ditching JQueryDitching JQuery
Ditching JQuery
 
Jsf Ajax
Jsf AjaxJsf Ajax
Jsf Ajax
 
Effectively Testing Services - Burlington Ruby Conf
Effectively Testing Services - Burlington Ruby ConfEffectively Testing Services - Burlington Ruby Conf
Effectively Testing Services - Burlington Ruby Conf
 
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScriptjQuery & 10,000 Global Functions: Working with Legacy JavaScript
jQuery & 10,000 Global Functions: Working with Legacy JavaScript
 
P H P Part I I, By Kian
P H P  Part  I I,  By  KianP H P  Part  I I,  By  Kian
P H P Part I I, By Kian
 
Test driven APIs with Laravel
Test driven APIs with LaravelTest driven APIs with Laravel
Test driven APIs with Laravel
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
DOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQueryDOM Scripting Toolkit - jQuery
DOM Scripting Toolkit - jQuery
 
JSF 2.0 Preview
JSF 2.0 PreviewJSF 2.0 Preview
JSF 2.0 Preview
 
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...
You don’t know JS about SharePoint - Mastering javascript performance (Hugh W...
 

Plus de Jay Phelps

Backpressure? Resistance is not futile. RxJS Live 2019
Backpressure? Resistance is not futile. RxJS Live 2019Backpressure? Resistance is not futile. RxJS Live 2019
Backpressure? Resistance is not futile. RxJS Live 2019Jay Phelps
 
Why I Love JSX!
Why I Love JSX!Why I Love JSX!
Why I Love JSX!Jay Phelps
 
ES2015 and Beyond
ES2015 and BeyondES2015 and Beyond
ES2015 and BeyondJay Phelps
 
Intro to Ember CLI
Intro to Ember CLIIntro to Ember CLI
Intro to Ember CLIJay Phelps
 
Ember Overview in 5 Minutes
Ember Overview in 5 MinutesEmber Overview in 5 Minutes
Ember Overview in 5 MinutesJay Phelps
 
Profit From Your Media Library Using Multi-Platform Distribution
Profit From Your Media Library Using Multi-Platform DistributionProfit From Your Media Library Using Multi-Platform Distribution
Profit From Your Media Library Using Multi-Platform DistributionJay Phelps
 
Intro to Ember.js
Intro to Ember.jsIntro to Ember.js
Intro to Ember.jsJay Phelps
 

Plus de Jay Phelps (7)

Backpressure? Resistance is not futile. RxJS Live 2019
Backpressure? Resistance is not futile. RxJS Live 2019Backpressure? Resistance is not futile. RxJS Live 2019
Backpressure? Resistance is not futile. RxJS Live 2019
 
Why I Love JSX!
Why I Love JSX!Why I Love JSX!
Why I Love JSX!
 
ES2015 and Beyond
ES2015 and BeyondES2015 and Beyond
ES2015 and Beyond
 
Intro to Ember CLI
Intro to Ember CLIIntro to Ember CLI
Intro to Ember CLI
 
Ember Overview in 5 Minutes
Ember Overview in 5 MinutesEmber Overview in 5 Minutes
Ember Overview in 5 Minutes
 
Profit From Your Media Library Using Multi-Platform Distribution
Profit From Your Media Library Using Multi-Platform DistributionProfit From Your Media Library Using Multi-Platform Distribution
Profit From Your Media Library Using Multi-Platform Distribution
 
Intro to Ember.js
Intro to Ember.jsIntro to Ember.js
Intro to Ember.js
 

Dernier

online pdf editor software solutions.pdf
online pdf editor software solutions.pdfonline pdf editor software solutions.pdf
online pdf editor software solutions.pdfMeon Technology
 
Growing Oxen: channel operators and retries
Growing Oxen: channel operators and retriesGrowing Oxen: channel operators and retries
Growing Oxen: channel operators and retriesSoftwareMill
 
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...OnePlan Solutions
 
How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?AmeliaSmith90
 
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...Jaydeep Chhasatia
 
Top Software Development Trends in 2024
Top Software Development Trends in  2024Top Software Development Trends in  2024
Top Software Development Trends in 2024Mind IT Systems
 
ERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxAutus Cyber Tech
 
Cybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadCybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadIvo Andreev
 
Fields in Java and Kotlin and what to expect.pptx
Fields in Java and Kotlin and what to expect.pptxFields in Java and Kotlin and what to expect.pptx
Fields in Java and Kotlin and what to expect.pptxJoão Esperancinha
 
Introduction-to-Software-Development-Outsourcing.pptx
Introduction-to-Software-Development-Outsourcing.pptxIntroduction-to-Software-Development-Outsourcing.pptx
Introduction-to-Software-Development-Outsourcing.pptxIntelliSource Technologies
 
Streamlining Your Application Builds with Cloud Native Buildpacks
Streamlining Your Application Builds  with Cloud Native BuildpacksStreamlining Your Application Builds  with Cloud Native Buildpacks
Streamlining Your Application Builds with Cloud Native BuildpacksVish Abrams
 
OpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorOpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorShane Coughlan
 
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLBig Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLAlluxio, Inc.
 
Why Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfWhy Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfBrain Inventory
 
Watermarking in Source Code: Applications and Security Challenges
Watermarking in Source Code: Applications and Security ChallengesWatermarking in Source Code: Applications and Security Challenges
Watermarking in Source Code: Applications and Security ChallengesShyamsundar Das
 
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfTobias Schneck
 
Enterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze IncEnterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze Incrobinwilliams8624
 
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageSales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageDista
 
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsYour Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsJaydeep Chhasatia
 

Dernier (20)

online pdf editor software solutions.pdf
online pdf editor software solutions.pdfonline pdf editor software solutions.pdf
online pdf editor software solutions.pdf
 
Growing Oxen: channel operators and retries
Growing Oxen: channel operators and retriesGrowing Oxen: channel operators and retries
Growing Oxen: channel operators and retries
 
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...
Transforming PMO Success with AI - Discover OnePlan Strategic Portfolio Work ...
 
How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?How Does the Epitome of Spyware Differ from Other Malicious Software?
How Does the Epitome of Spyware Differ from Other Malicious Software?
 
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...
Optimizing Business Potential: A Guide to Outsourcing Engineering Services in...
 
Top Software Development Trends in 2024
Top Software Development Trends in  2024Top Software Development Trends in  2024
Top Software Development Trends in 2024
 
ERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptxERP For Electrical and Electronics manufecturing.pptx
ERP For Electrical and Electronics manufecturing.pptx
 
Cybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and BadCybersecurity Challenges with Generative AI - for Good and Bad
Cybersecurity Challenges with Generative AI - for Good and Bad
 
Fields in Java and Kotlin and what to expect.pptx
Fields in Java and Kotlin and what to expect.pptxFields in Java and Kotlin and what to expect.pptx
Fields in Java and Kotlin and what to expect.pptx
 
Introduction-to-Software-Development-Outsourcing.pptx
Introduction-to-Software-Development-Outsourcing.pptxIntroduction-to-Software-Development-Outsourcing.pptx
Introduction-to-Software-Development-Outsourcing.pptx
 
Streamlining Your Application Builds with Cloud Native Buildpacks
Streamlining Your Application Builds  with Cloud Native BuildpacksStreamlining Your Application Builds  with Cloud Native Buildpacks
Streamlining Your Application Builds with Cloud Native Buildpacks
 
OpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS CalculatorOpenChain Webinar: Universal CVSS Calculator
OpenChain Webinar: Universal CVSS Calculator
 
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/MLBig Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
Big Data Bellevue Meetup | Enhancing Python Data Loading in the Cloud for AI/ML
 
Why Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdfWhy Choose Brain Inventory For Ecommerce Development.pdf
Why Choose Brain Inventory For Ecommerce Development.pdf
 
Watermarking in Source Code: Applications and Security Challenges
Watermarking in Source Code: Applications and Security ChallengesWatermarking in Source Code: Applications and Security Challenges
Watermarking in Source Code: Applications and Security Challenges
 
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdfARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
ARM Talk @ Rejekts - Will ARM be the new Mainstream in our Data Centers_.pdf
 
Enterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze IncEnterprise Document Management System - Qualityze Inc
Enterprise Document Management System - Qualityze Inc
 
Salesforce AI Associate Certification.pptx
Salesforce AI Associate Certification.pptxSalesforce AI Associate Certification.pptx
Salesforce AI Associate Certification.pptx
 
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales CoverageSales Territory Management: A Definitive Guide to Expand Sales Coverage
Sales Territory Management: A Definitive Guide to Expand Sales Coverage
 
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software TeamsYour Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
Your Vision, Our Expertise: TECUNIQUE's Tailored Software Teams
 

RxJS + Redux + React = Amazing

  • 1. RxJS + Redux + React = Amazing Jay Phelps | @_jayphelps Side Effect Management with RxJS
  • 2. Jay Phelps | @_jayphelps Managing state stuff is hard
  • 3. Jay Phelps | @_jayphelps Redux makes it simple (not necessarily easy)
  • 4. Jay Phelps | @_jayphelps Managing async stuff is harder
  • 5. Jay Phelps | @_jayphelps Some async is complex regardless of the abstraction
  • 6. Jay Phelps | @_jayphelps RxJS makes it manageable
  • 7. Jay Phelps Senior Software Engineer | @_jayphelps
  • 8. What is redux? Jay Phelps | @_jayphelps
  • 9. Crash Course Jay Phelps | @_jayphelps
  • 10. What is redux? Jay Phelps | @_jayphelps Provides predicable state management using actions and reducers
  • 11. What's an "action"? Jay Phelps | @_jayphelps Describes something has (or should) happen, but they don't specify how it should be done
  • 12. Jay Phelps | @_jayphelps { type: 'CREATE_TODO', payload: 'Build my first Redux app' }
  • 13. What's an "reducer"? Jay Phelps | @_jayphelps A pure function that takes the previous state and an action and returns the new state
  • 14. What's an "reducer"? Jay Phelps | @_jayphelps Sometimes it returns the previous state (state, action) => state
  • 15. What's an "reducer"? Jay Phelps | @_jayphelps Sometimes it computes new state (state, action) => state + action.payload
  • 16. Jay Phelps | @_jayphelps const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } };
  • 17. Jay Phelps | @_jayphelps Reducers handle state transitions, but they must be done synchronously.
  • 18. Jay Phelps | @_jayphelps const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } };
  • 19. Jay Phelps | @_jayphelps What are async stuff do we commonly do?
  • 20. Async Jay Phelps | @_jayphelps • User interactions (mouse, keyboard, etc) • AJAX • Timers/Animations • Web Sockets • Work Workers, etc
  • 21. Jay Phelps | @_jayphelps Some can be handled synchronously
  • 22. Jay Phelps | @_jayphelps <button onClick={() => dispatch({ type: 'INCREMENT' })}> Increment </button>
  • 23. Jay Phelps | @_jayphelps const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } };
  • 24. Jay Phelps | @_jayphelps Sometimes you need more control
  • 25. Jay Phelps | @_jayphelps • AJAX cancellation/composing • Debounce/throttle/buffer/etc • Drag and Drop • Web Sockets, Work Workers, etc
  • 26. Jay Phelps | @_jayphelps Use middleware to manage async / side effects
  • 27. Jay Phelps | @_jayphelps Most of them use callbacks or Promises
  • 28. Callbacks Jay Phelps | @_jayphelps The most primitive way to handle async in JavaScript
  • 29. Callbacks Jay Phelps | @_jayphelps fetchSomeData((error, data) => { if (!error) { dispatch({ type: 'HERES_THE_DATA', data }); } });
  • 30. Callback Hell Jay Phelps | @_jayphelps fetchSomeData(id, (error, data) => { if (!error) { dispatch({ type: 'HERES_THE_FIRST_CALL_DATA', data }); fetchSomeData(data.parentId, (error, data) => { if (!error) { dispatch({ type: 'HERES_SOME_MORE', data }); fetchSomeData(data.parentId, (error, data) => { if (!error) { dispatch({ type: 'OMG_MAKE_IT_STOP', data }); } }); } }); } });
  • 31. Promises Jay Phelps | @_jayphelps fetchSomeData(id) .then(data => { dispatch({ type: 'HERES_THE_FIRST_CALL_DATA', data }); return fetchSomeData(data.parentId); }) .then(data => { dispatch({ type: 'HERES_SOME_MORE', data }); return fetchSomeData(data.parentId); }) .then(data => { dispatch({ type: 'OKAY_IM_DONE', data }); });
  • 32. Promises Jay Phelps | @_jayphelps • Guaranteed future • Immutable • Single value • Caching
  • 33. Promises Jay Phelps | @_jayphelps • Guaranteed future • Immutable • Single value • Caching
  • 34. Promises Jay Phelps | @_jayphelps • Guaranteed future • Immutable • Single value • Caching
  • 35. Jay Phelps | @_jayphelps Promises cannot be cancelled
  • 36. Jay Phelps | @_jayphelps Why would you want to cancel?
  • 37. Jay Phelps | @_jayphelps • Changing routes/views • Auto-complete • User wants you to
  • 38. Jay Phelps | @_jayphelps • Changing routes/views • Auto-complete • User wants you to
  • 39. Jay Phelps | @_jayphelps Daredevil
  • 40. Jay Phelps | @_jayphelps Daredevil The Get Down Here’s Daredevil!
  • 41. Jay Phelps | @_jayphelps Daredevil
  • 42. Jay Phelps | @_jayphelps Daredevil The Get Down
  • 43. Jay Phelps | @_jayphelps Cancelling is common and often overlooked
  • 44. Promises Jay Phelps | @_jayphelps • Guaranteed future • Immutable • Single value • Caching
  • 45. Only AJAX is single value Jay Phelps | @_jayphelps • User interactions (mouse, keyboard, etc • AJAX • Animations • WebSockets, Workers, etc
  • 46. Jay Phelps | @_jayphelps What do we use?
  • 47. Jay Phelps | @_jayphelps Observables
  • 48. Observables Jay Phelps | @_jayphelps • Stream of zero, one, or more values • Over any amount of time • Cancellable
  • 49. Jay Phelps | @_jayphelps Streams are a set, with a dimension of time
  • 50. Jay Phelps | @_jayphelps Being standardized for ECMAScript aka JavaScript
  • 51. RxJS Jay Phelps | @_jayphelps “lodash for async” - Ben Lesh
  • 52. Crash Course Jay Phelps | @_jayphelps
  • 53. Creating Observables Jay Phelps | @_jayphelps • of('hello') • from([1, 2, 3, 4]) • interval(1000) • ajax('http://example.com') • webSocket('ws://echo.websocket.com') • fromEvent(button, ‘click') • many more…
  • 54. Subscribing Jay Phelps | @_jayphelps myObservable.subscribe( value => console.log('next', value) );
  • 55. Subscribing Jay Phelps | @_jayphelps myObservable.subscribe( value => console.log('next', value), err => console.error('error', err) );
  • 56. Subscribing Jay Phelps | @_jayphelps myObservable.subscribe( value => console.log('next', value), err => console.error('error', err), () => console.info('complete!') );
  • 57. Observables can be transformed Jay Phelps | @_jayphelps map, filter, reduce
  • 58. Observables can be combined Jay Phelps | @_jayphelps concat, merge, zip
  • 59. Observables represent time Jay Phelps | @_jayphelps debounce, throttle, buffer, combineLatest
  • 60. Observables are lazy Jay Phelps | @_jayphelps retry, repeat
  • 61. Jay Phelps | @_jayphelps Observables can represent just about anything
  • 62. Jay Phelps | @_jayphelps Let’s combine RxJS and Redux!
  • 63. Jay Phelps | @_jayphelps
  • 65. Side effect management for redux, using Epics
  • 66. What is an Epic? Jay Phelps | @_jayphelps A function that takes a stream of all actions dispatched and returns a stream of new actions to dispatch
  • 67. Jay Phelps | @_jayphelps “actions in, actions out”
  • 71. An Epic Jay Phelps | @_jayphelps const pingPongEpic = (action$, store) => action$.ofType('PING') .delay(1000) // <— that's it .map(action => ({ type: 'PONG' }));
  • 72. Jay Phelps | @_jayphelps const isPinging = (state = false, action) => { switch (action.type) { case 'PING': return true; case 'PONG': return false; default: return state; } };
  • 74. Jay Phelps | @_jayphelps Debounced increment / decrement button
  • 75. Jay Phelps | @_jayphelps const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } };
  • 76. Jay Phelps | @_jayphelps const incrementEpic = (action$, store) => action$.ofType('INCREMENT_DEBOUNCED') .debounceTime(1000) .map(() => ({ type: 'INCREMENT' })); const decrementEpic = (action$, store) => action$.ofType('DECREMENT_DEBOUNCED') .debounceTime(1000) .map(() => ({ type: 'DECREMENT' }));
  • 77. Jay Phelps | @_jayphelps const incrementEpic = (action$, store) => action$.ofType('INCREMENT_DEBOUNCED') .debounceTime(1000) .map(() => ({ type: 'INCREMENT' })); const decrementEpic = (action$, store) => action$.ofType('DECREMENT_DEBOUNCED') .debounceTime(1000) .map(() => ({ type: 'DECREMENT' }));
  • 78. Jay Phelps | @_jayphelps Those are contrived examples, obviously
  • 79. Jay Phelps | @_jayphelps Warning: non-trivial examples ahead, don’t struggle to read them entirely
  • 80. Jay Phelps | @_jayphelps Auto-complete
  • 81. Jay Phelps | @_jayphelps onKeyUp(e) { const { store } = this.props; const { value } = e.target.value; if (this.queryId) { clearTimeout(this.queryId); } this.queryId = setTimeout(() => { if (this.xhr) { this.xhr.abort(); } const xhr = this.xhr = new XMLHttpRequest(); xhr.open('GET', 'https://api.github.com/search/users?q=' + value); xhr.onload = () => { if (xhr.status === 200) { store.dispatch({ type: 'QUERY_FULFILLED', payload: JSON.parse(xhr.response).items }); } else { store.dispatch({ type: 'QUERY_REJECTED', error: true, payload: { message: xhr.response, status: xhr.status } }); } }; xhr.send(); }, 500); } Plain JS
  • 82. Jay Phelps | @_jayphelps Epic const autoCompleteEpic = (action$, store) => action$.ofType('QUERY') .debounceTime(500) .switchMap(action => ajax('https://api.github.com/search/users?q=' + value) .map(payload => ({ type: 'QUERY_FULFILLED', payload })) );
  • 83. Jay Phelps | @_jayphelps Epic const autoCompleteEpic = (action$, store) => action$.ofType('QUERY') .debounceTime(500) .switchMap(action => ajax('https://api.github.com/search/users?q=' + value) .map(payload => ({ type: 'QUERY_FULFILLED', payload })) .catch(payload => [{ type: 'QUERY_REJECTED', error: true, payload }]) );
  • 84. Jay Phelps | @_jayphelps Epic const autoCompleteEpic = (action$, store) => action$.ofType('QUERY') .debounceTime(500) .switchMap(action => ajax('https://api.github.com/search/users?q=' + value) .map(payload => ({ type: 'QUERY_FULFILLED', payload })) .takeUntil(action$.ofType('CANCEL_QUERY')) .catch(payload => [{ type: 'QUERY_REJECTED', error: true, payload }]) );
  • 85. Jay Phelps | @_jayphelps OK, show me really non-trivial examples
  • 86. Jay Phelps | @_jayphelps Bidirectional, multiplexed Web Sockets
  • 87. Jay Phelps | @_jayphelps class Example { @autobind checkChange(e) { const { value: key, checked } = e.target; this.subs = this.subs || []; if (checked) { const handler = e => { const data = JSON.parse(e.data); if (data.key === key) { this.updateValue(key, data.value); } }; this.subs.push({ key, handler }) const socket = this.getSocket(() => { this.setState({ socketOpen: true }); this.subs.forEach(({ key }) => socket.send(JSON.stringify({ type: 'sub', key}))); }); socket.addEventListener('message', handler); } else { const index = this.subs.findIndex(x => x.key === key); if (index !== -1) { this.subs.splice(index, 1); } const { socket } = this; if (socket && socket.readyState === 1) { socket.send(JSON.stringify({ type: 'unsub', key })); this.setInactive(key)l if (this.subs.length === 0) { socket.close(); } } } } componentWillUnMount() { if (this.socket && this.socket.readyState === 1) { this.socket.close(); } } Plain JS getSocket(callback) { const { socket } = this; if (socket && socket.readyState === 1) { setTimeot(callback); } else { if (this.reconnectId) { clearTimeout(this.reconnectId); } socket = this.socket = new WebSocket(‘ws://localhost:3000'); socket.onopen = () => { callback(); }; socket.onerror = () => { this.reconnectId = setTimeout(() => this.getSocket(callback), 1000); this.setState({ socketOpen: false }); }; socket.onclose = (e) => { if (!e.wasClean) { this.reconnectId = setTimeout(() => this.getSocket(callback), 1000); } this.setState({ socketOpen: false }); }; } return socket; } }
  • 88. Jay Phelps | @_jayphelps Too much code
  • 89. Jay Phelps | @_jayphelps As an Epic const socket = WebSocketSubject.create('ws://stock/endpoint'); const stockTickerEpic = (action$, store) => action$.ofType('START_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 })) );
  • 90. redux-observable Jay Phelps | @_jayphelps • Makes it easier to compose and control complex async tasks, over any amount of time • You don't need to manage your own Rx subscriptions • You can use redux tooling
  • 91. But… Jay Phelps | @_jayphelps
  • 92. Jay Phelps | @_jayphelps You should probably know redux and RxJS in advance
  • 93. Jay Phelps | @_jayphelps RxJS has a bit of a learning curve
  • 94. Jay Phelps | @_jayphelps “Reactive Programming”
  • 95. Co-author Jay Phelps | @_jayphelps Ben Lesh Senior UI Engineer | @benlesh
  • 96. Jay Phelps | @_jayphelps https://redux-observable.js.org