42
The Saga Begins Daniel Franz

The redux saga begins

Embed Size (px)

Citation preview

The Saga BeginsDaniel Franz

A long, long time ago...

● We were using redux-thunk

● We were chaining actions in actions

● We were providing callbacks to actions

● We were afraid of writing tests

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction() { return (dispatch) => { dispatch(someActionPending()); someAsyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction()); }) .error((error) => dispatch(someActionError(error)); }}

redux-thunkfunction someAction(followUpAction) { return (dispatch) => { dispatch(someActionPending()); asyncFunction() .then((result) => { dispatch(someActionSuccess(result)); dispatch(followUpAction(result)); }) .error((error) => dispatch(someActionError(error)); }}

CALLBACK HELL

Advantages of Redux-Saga

Pure actions

Power of middleware with concise syntax

Readable and testable workflows

Side effects encapsulated in saga

Return from callback hell

Refactored to Redux-Sagafunction* someSaga() { yield put(someActionPending()); try { const result = yield call(someAsyncFunction); yield put(someActionSuccess(result)); yield* followUpSaga(); } catch (error) { yield put(someActionError(error)); }}

function* watchSomeSaga() { yield takeEvery(TYPES.SOME_ACTION, someSaga);}

Refactored to Redux-Sagafunction* someSaga() { yield put(someActionPending()); try { const result = yield call(someAsyncFunction); yield put(someActionSuccess(result)); yield* followUpSaga(); } catch (error) { yield put(someActionError(error)); }}

function* watchSomeSaga() { yield takeEvery(TYPES.SOME_ACTION, someSaga);}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Leonard Koch
Ich glaube ich würde hier nicht next(5) callen sondern einfach mehrfach next. Das ist glaube ich eingängiger.
Leonard Koch
Vielleicht ist es mit ausreichender Erklärung okay. Not sure.
Daniel Franz
Was hälst du von diesem Beispiel? Auf der folgenden Seite ist dann ein Bi-direktionales Beispiel. Den eingefärbten Flow würde ich dann später machen
Leonard Koch
Das sieht super aus.

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

ES6-Generatorsfunction* generator (i) {

yield i; i = i + 5;

yield i;}

let g = generator(3);g.next(); // {value: 3, done: false}g.next(); // {value: 8, done: false}g.next(); // {value: undefined, done: true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Leonard Koch
Auch ein gutes Beispiel. Würde hier dann in Worten ganz klar machen, dass wenn man an der zweiten Stelle dort ein Argument reingibt es dann den vorherigen yield wert ersetzt.Das ist relativ unintuitiv.
Daniel Franz
Ja, es ist im ersten Augenblick nicht intuitiv. Wenn man den Flow durchgeht, wird beim yield unterbrochen. Die Zuweisung zu i passiert ja erst danach. Glaube das wird durch die Färbung relativ klar.
Daniel Franz
Natürlich ist alles erklärungsbedürftig und ich werde auch viele Worte dazu sagen :)
Leonard Koch
Cool. Ja klar. Das sieht ziemlich gut aus. Hast du den Durchlauf gemacht?
Daniel Franz
Ja, habe 13 Minuten für die Slides gebraucht. Wird sich sicherlich noch verändern, wenn ich alles sage, was ich sagen möchte, und sicherer/schneller werde ;)
Leonard Koch
top
Leonard Koch
dann hast du ja noch gut Raum fürs live coding und brauchst dir nicht groß Sorgen um die Zeit machen.

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Bi-directional ES6-Generatorsfunction* generator () {

let i = yield; i = i + 5;

yield i;}

let g = generator();g.next(); // {value: undefined, done: false}g.next(3); // {value: 8, done: false}g.next(); // {value: undefined: done, true}

Combining Generatorsfunction* anotherGenerator(i) { yield i + 1; yield i + 2;}

function* generator(i) { yield i; yield* anotherGenerator(i); yield i + 10;}

let g = generator(10);

g.next().value; // 10g.next().value; // 11g.next().value; // 12g.next().value; // 20

Back to Redux-Sagafunction* someSaga() { yield put(someActionPending()); try { const result = yield call(asyncFunction); yield put(someActionSuccess(result)); yield* followUpSaga(); } catch (error) { yield put(someActionError(error)); }}

function* watchSomeSaga() { yield takeEvery(TYPES.TRIGGER_ACTION, someSaga);}

Saga Effectsfunction* someSaga() { yield put(someActionPending()); try { const result = yield call(asyncFunction); yield put(someActionSuccess(result)); yield* followUpSaga(); } catch (error) { yield put(someActionError(error)); }}

function* watchSomeSaga() { yield takeEvery(TYPES.TRIGGER_ACTION, someSaga);}

Examples of effects:

● put dispatches an action

● take waits for an action

● call calls an (async) function

● select gets (partial) state

Daniel Franz
was hälst du von den erklärungen?
Felix Mau
sind okay, vllt nocheinml takeLatest / takeEvery erklären?
Felix Mau
Ah nächste Folie okay 🙈

Saga Combined Effectsfunction* someSaga() { yield put(someActionPending()); try { const result = yield call(asyncFunction); yield put(someActionSuccess(result)); yield* followUpSaga(); } catch (error) { yield put(someActionError(error)); }}

function* watchSomeSaga() { yield takeEvery(TYPES.TRIGGER_ACTION, someSaga);}

Examples of combined effects:

● takeEvery waits for an action,

executes a generator and

waits for the next action

● takeLatest works like

takeEvery, but cancels the

execution of the generator

when another action arrives

Debouncing sagafunction* debounceSaga(action) { yield call(delay, 300); yield put(keyDownAction(action.value));}

function* watchDebounceActionSaga() { yield takeLatest(TYPES.DEBOUNCE_KEY_DOWN_ACTION, debounceSaga);}

Daniel Franz
Wie ist das Beispiel?
Felix Mau
Gutes Beispiel für takeLatest!

Pollingfunction* workerSaga() { const something = yield call(someService, fetchSomething); yield put(someAction(something));}

function* pollingSaga() { while (true) { yield* workerSaga(); yield call(delay, 5000); }}

Daniel Franz
Wie ist das Beispiel?

Testing Live Demo

Leonard Koch
Anhängig davon wie die live demo ist, könnte man hier auch noch mit slides etwas klar machen wie das mit dem testing funktioniert. In ähnlicher Form wie du es für generators gemacht hast. Manchmal haben Leute Schwierigkeiten das im live coding zu begreifen.Wenn du es langsam genug machst und genug erklärst, sollte es aber okay sein.
Daniel Franz
Habe jetzt die 3 Folien davor hier eingefügt. Wie bereits gefragt: Was hälst du davon? ;)

Concusion

Side effects encapsulated in saga

Power of middleware with concise syntax

Readable and easily testable workflows

Pure actions

Thank you

Questions?