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

Reactive Programming with React

Reactive Programming with React

The talk I gave at the "Battle of the Titans" meetup at Spilberg.

Avatar for Robin van der Vleuten

Robin van der Vleuten

June 19, 2017

Other Decks in Technology

Transcript

  1. { "name": "Robin van der Vleuten", "role": "Indepentent Developer", "stack":

    [ "PHP", "Symfony", "Laravel", "Javascript", "React (+ Preact)”, "Node.js", "Hapi.js" ], "company": "The Webstronauts", "website": "www.robinvdvleuten.nl", "social": { "twitter": "@robinvdvleuten", "github": "robinvdvleuten" } }
  2. (x) => { return x + 1; } (x, y)

    => { return x + y; }
  3. const addToCart = (cart, item, quantity) => { cart.push({ item,

    quantity }); return cart; }; an “impure” function
  4. const addToCart = (cart, item, quantity) => { return [

    ...cart, { item, quantity }]; }; a “pure” function
  5. DISPATCHING ACTIONS const increaseCounter = () => ({ type: 'INCREASE'

    }) <button onClick={() => dispatch(increaseCounter())}> Increase Counter </button>
  6. DISPATCHING ACTIONS const increaseeCounter = () => ({ type: 'INCREASE'

    }) <button onClick={props.increaseCounter}> Increase Counter </button> connect(null, { increaseCounter })(button)
  7. const counter = (state = 0, action) => { switch

    (action.type) { case 'INCREASE': return state + 1; case 'DECREASE': return state - 1; default: return state; } }
  8. MIDDLEWARE const logger = store => next => action =>

    { console.log('dispatching', action) let result = next(action) console.log('next state', store.getState()) return result }
  9. export const fetchAllAsyncData = () => { return dispatch =>

    { fetchAsyncData(id, (err, data) => { if (err) { dispatch({ type: 'ASYNC_FAILURE', err }); } fetchAsyncData(data.parent, (err, data) => { if (err) { dispatch({ type: 'ASYNC_FAILURE', err }); } fetchAsyncData(data.parent, (err, data) => { if (err) { dispatch({ type: 'ASYNC_FAILURE', err }); } dispatch({ type: 'ASYNC_SUCCESS', data }); }); }); }); } }
  10. export const fetchAllAsyncData = () => { return dispatch =>

    { return fetchAsyncData(id) .then(data => { return fetchAsyncData(data.parent); }) .then(data => { return fetchAsyncData(data.parent); }) .then(data => { dispatch({ type: 'ASYNC_SUCCESS', data }); }) .catch(err => { dispatch({ type: 'ASYNC_FAILURE', err }); }); } }
  11. • fulfilled - The action relating to the promise succeeded

    • rejected - The action relating to the promise failed • pending - Hasn't fulfilled or rejected yet • settled - Has fulfilled or rejected And a Promise can only return a single value A Promise can be
  12. /* Get stock data somehow synchronous */ const source =

    getSyncStockData(); const subscription = source .filter(quote => quote.price > 30) .map(quote => quote.price) .forEach(price => console.log(`Prices higher than $30: ${price}`));
  13. /* Get stock data somehow asynchronous */ const source =

    getAsyncStockData(); const subscription = source .filter(quote => quote.price > 30) .map(quote => quote.price) .subscribe( price => console.log(`Prices higher than $30: ${price}`), err => console.log(`Something went wrong: ${err.message}`) ); /* When we're done */ subscription.dispose()
  14. const pingEpic = action$ => action$.filter(action => action.type === 'PING')

    .mapTo({ type: 'PONG' }); // later... dispatch({ type: 'PING' });
  15. const pingEpic = action$ => action$.ofType(‘PING') .delay(1000) // Asynchronously wait

    1000ms then continue .mapTo({ type: 'PONG' }); // later... dispatch({ type: 'PING' });
  16. // action creators const fetchUser = username => ({ type:

    FETCH_USER, payload: username }); const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload }); // epic const fetchUserEpic = action$ => action$.ofType(FETCH_USER) .mergeMap(action => ajax.getJSON(`https://api.github.com/users/${action.payload}`) .map(response => fetchUserFulfilled(response)) ); // later... dispatch(fetchUser('robinvdvleuten'));
  17. describe('fetchUserEpic', () => { it('should return an user object on

    success', async () => { expect.assertions(2); const user = { username: 'robinvdvleuten' }; const response = Observable.of(forms); const ajax = { getJSON: jest.fn() }; ajax.getJSON.mockReturnValueOnce(response); const actions$ = ActionsObservable.of(fetchUser('robinvdvleuten')); const actions = await epics(actions$, store, { ajax }) .toArray() .toPromise(); expect(actions).toEqual([ { type: FETCH_USER, payload: 'robinvdvleuten' }, { type: FETCH_USER_FULFILLED, user }, ]); expect(request).toHaveBeenCalledWith('https://api.github.com/users/robinvdvleuten'); }); });
  18. // action creators const fetchUser = username => ({ type:

    FETCH_USER, payload: username }); const fetchUserFulfilled = payload => ({ type: FETCH_USER_FULFILLED, payload }); // epic const fetchUserEpic = (action$, getStore, { ajax }) => action$.ofType(FETCH_USER) .mergeMap(action => ajax.getJSON(`https://api.github.com/users/${action.payload}`) .map(response => fetchUserFulfilled(response)) ); // later... dispatch(fetchUser('robinvdvleuten'));