Slide 1

Slide 1 text

Redux-Thunk Radoslav Stankov 26/07/2016

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

Reducer Action React View Store

Slide 6

Slide 6 text

Reducer Action React View New Store

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

class Filters extends React.Component { filterHander(filterName) { return () => { this.props.dispatch(changeFilter(filterName)); }; } render() { return (
{Object.keys(FILTERS).map((filter) => ( {filter} ))}
); } }

Slide 9

Slide 9 text

function changeFilter(filterName) { dispatch({ type: 'TODO/CHANGE_FILTER', filter: filter }); }

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

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 (
{Object.keys(FILTERS).map((filter) => ( {filter} ))}
); } }

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

redux-thunk https://github.com/gaearon/redux-thunk

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

function action() { return async (dispatch, getState) => { // do ... async stuff dispatch({ type: ACTION_1 }); // do ... async stuff dispatch({ type: ACTION_2 }); // ...so on }; } redux-thunk

Slide 18

Slide 18 text

import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import reducer from './reducers'; const store = createStore(rootReducer, applyMiddleware(thunk));

Slide 19

Slide 19 text

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 }); }; }

Slide 20

Slide 20 text

this.props.filterChange(filterName, this.props.api);

Slide 21

Slide 21 text

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 }); }; }

Slide 22

Slide 22 text

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));

Slide 23

Slide 23 text

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 }); }; }

Slide 24

Slide 24 text

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 }); }; }

Slide 25

Slide 25 text

class Filters extends React.Component { filterHander(filterName) { return () => { this.props.dispatch(changeFilter(filterName)); }; } render() { return (
{Object.keys(FILTERS).map((filter) => ( {filter} ))}
); } }

Slide 26

Slide 26 text

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([]); }); });

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

redux-saga https://github.com/yelouafi/redux-saga

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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); }

Slide 31

Slide 31 text

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);

Slide 32

Slide 32 text

https://speakerdeck.com/rstankov/redux-thunk

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

@rstankov Thanks :)