Redux-Thunk

 Redux-Thunk

Lightning talk about handling async tasks in redux with redux-thunk.

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

July 26, 2016
Tweet

Transcript

  1. Redux-Thunk Radoslav Stankov 26/07/2016

  2. Radoslav Stankov @rstankov http://rstankov.com http://github.com/rstankov

  3. None
  4. None
  5. Reducer Action React View Store

  6. Reducer Action React View New Store

  7. None
  8. class Filters extends React.Component { filterHander(filterName) { return () =>

    { this.props.dispatch(changeFilter(filterName)); }; } render() { return ( <div> {Object.keys(FILTERS).map((filter) => ( <button key={filter} onClick={this.filterHander(filter)}> {filter} </button> ))} </div> ); } }
  9. function changeFilter(filterName) { dispatch({ type: 'TODO/CHANGE_FILTER', filter: filter }); }

  10. None
  11. None
  12. None
  13. class Filters extends React.Component { filterHander(filterName) { return async ()

    => { this.props.dispatch(changeFilter(filterName)); const todos = await this.props.api.loadTodos({filter: filter}); this.props.dispatch(setTodos(todos)); }; } render() { return ( <div> {Object.keys(FILTERS).map((filter) => ( <button key={filter} onClick={this.filterHander(filter)}> {filter} </button> ))} </div> ); } }
  14. None
  15. redux-thunk https://github.com/gaearon/redux-thunk

  16. function action() { return { type: ACTION }; } redux-thunk

  17. function action() { return async (dispatch, getState) => { //

    do ... async stuff dispatch({ type: ACTION_1 }); // do ... async stuff dispatch({ type: ACTION_2 }); // ...so on }; } redux-thunk
  18. import { createStore, applyMiddleware } from 'redux'; import thunk from

    'redux-thunk'; import reducer from './reducers'; const store = createStore(rootReducer, applyMiddleware(thunk));
  19. function changeFilter(filterName, api) { return async function(dispatch, getState) { if

    (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }
  20. this.props.filterChange(filterName, this.props.api);

  21. function changeFilter(filterName, api) { return async function(dispatch, getState) { if

    (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }
  22. import { createStore, applyMiddleware } from 'redux'; import thunk from

    'redux-thunk'; import reducer from './reducers'; import apiClient from './utils/apiClient'; const api = apiClient(); const store = createStore(rootReducer, applyMiddleware(thunk.withExtraArgument(api));
  23. function changeFilter(filterName, api) { return async function(dispatch, getState) { if

    (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }
  24. function changeFilter(filterName) { return async function(dispatch, getState, api) { if

    (getState().filter == filter) { return; } dispatch({ type: 'TODO/LOAD', filter: filter }); const todos = await api.loadTodos({filter: filter}); dispatch({ type: 'TODO/LOADED', todos: todos }); }; }
  25. class Filters extends React.Component { filterHander(filterName) { return () =>

    { this.props.dispatch(changeFilter(filterName)); }; } render() { return ( <div> {Object.keys(FILTERS).map((filter) => ( <button key={filter} onClick={this.filterHander(filter)}> {filter} </button> ))} </div> ); } }
  26. import createStore from 'tests/support/createStore'; import factory from 'tests/support/factory'; describe(changeFilter.name, ()

    => { it('loads todos', () => { const store = createStore({ filter: 'all' }); const todo = factory.todo(); store.api.stub('loadTodos', [todo]); store.dispatch(changeFilter('completed')); expect(store.getState().filter).to.equal('completed'); expect(store.getState().todos).to.deep.equal([todo]); }); it('does not reload todos when filter is not changing', () => { const store = createStore({ filter: 'all' }); const todo = factory.todo(); store.api.stub('loadTodos', [todo]); store.dispatch(changeFilter('all')); expect(store.getState().todos).to.deep.equal([]); }); });
  27. None
  28. redux-saga https://github.com/yelouafi/redux-saga

  29. redux-saga export default function changeFilter(filter) { return { type: 'TODO/SET_FILTER',

    filter: filter }; }
  30. redux-saga import { takeEvery } from 'redux-saga'; import { put

    } from 'redux-saga/effects'; function* loadFilter(action) { yield put({ type: 'TODO/LOADING' }); const todos = yield api.loadTodos({filter: action.filter}); yield put({ type: 'TODO/LOADED', todos: todos }); } export function* saga() { yield* takeEvery('TODO/SET_FILTER', loadFilter); }
  31. redux-saga import { createStore, applyMiddleware } from 'redux'; import createSagaMiddleware

    from 'redux-saga'; import reducer from './reducers'; import saga from './actions/changeFilter'; const sagaMiddleware = createSagaMiddleware(); const store = createStore(reducer, applyMiddleware(sagaMiddleware)); sagaMiddleware.run(saga);
  32. https://speakerdeck.com/rstankov/redux-thunk

  33. None
  34. @rstankov Thanks :)