|
@@ -0,0 +1,93 @@
|
|
|
+# Using Sagas
|
|
|
+<https://redux-saga.github.io/redux-saga/docs/introduction/BeginnerTutorial.html>
|
|
|
+
|
|
|
+* Sagas connect as middleware to a redux store.
|
|
|
+* Sagas run like cuncurrent worker tasks and have to be started for that.
|
|
|
+
|
|
|
+```jsx
|
|
|
+// main.js
|
|
|
+import { createStore, applyMiddleware } from 'redux'
|
|
|
+import createSagaMiddleware from 'redux-saga'
|
|
|
+
|
|
|
+import { rootSaga } from './sagas'
|
|
|
+
|
|
|
+const sagaMiddleware = createSagaMiddleware()
|
|
|
+const store = createStore(
|
|
|
+ reducer,
|
|
|
+ applyMiddleware(sagaMiddleware)
|
|
|
+)
|
|
|
+sagaMiddleware.run(helloSaga)
|
|
|
+```
|
|
|
+
|
|
|
+* Asynchronous actions need to be split into more tasks to the dispatcher, so reducers can always return immediately:
|
|
|
+
|
|
|
+```jsx
|
|
|
+// actions.js
|
|
|
+function increment(payload) {
|
|
|
+ return {
|
|
|
+ type: 'INCREMENT_REQUEST',
|
|
|
+ payload
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// reducers.js
|
|
|
+export default function counter(state = 0, action) {
|
|
|
+ switch (action.type) {
|
|
|
+ case 'INCREMENT_REQUEST':
|
|
|
+ // disable button
|
|
|
+ // print message
|
|
|
+ /* YOU DON'T NEED TO CALL THE FUNCTION HERE.
|
|
|
+ IT'S INTERCEPTED BY THE MIDDLEWARE. */
|
|
|
+ return state
|
|
|
+ case 'INCREMENT_SUCCESS':
|
|
|
+ return state + 1
|
|
|
+ case 'INCREMENT_FAIL':
|
|
|
+ // print error message
|
|
|
+ default:
|
|
|
+ return state
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// sagas.js
|
|
|
+import { call, put, takeEvery } from 'redux-saga/effects'
|
|
|
+
|
|
|
+// Worker
|
|
|
+export function * incrementAsync () {
|
|
|
+ try {
|
|
|
+ const data = yield call(Api.isIncrementOk)
|
|
|
+ yield put({ type: 'INCREMENT_SUCCESS', data })
|
|
|
+ } catch (error) {
|
|
|
+ yield put({ type: 'INCREMENT_FAIL', error})
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Watcher (intercepts INCREMENT_REQUEST, dispatches INCREMENT_SUCCESS or INCREMENT_FAIL in return.)
|
|
|
+export function * watchIncrementAsync() {
|
|
|
+ yield takeEvery('INCREMENT_REQUEST', incrementAsync)
|
|
|
+}
|
|
|
+
|
|
|
+// export all sagas in parallel
|
|
|
+export default function * rootSaga() {
|
|
|
+ yield takeEvery('INCREMENT_REQUEST')
|
|
|
+ yield takeEvery('DECREMENT_REQUEST')
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Saga helpers
|
|
|
+* Yield 'effects', in 'redux-saga/effects'
|
|
|
+* `takeEvery` handles every action of a type that is dispatched.
|
|
|
+* Allows multiple `incrementAsync` instances to be started concurrently.
|
|
|
+* `takeLatest` in contrast only takes the response of the latest requested action.
|
|
|
+* Allows only one `incrementAsync` to run at a time
|
|
|
+* If another task is requested, the previous is automatically cancelled.
|
|
|
+* You can start multiple sagas which behave like they were forked.
|
|
|
+
|
|
|
+## Declarative effects
|
|
|
+* Effects are plain JavaScript objects expressing Saga logic.
|
|
|
+* they contain information to be interpreted by the middleware.
|
|
|
+* Sagas can yield effects in multiple forms, the easiest are promises
|
|
|
+* Use `call` to call functions. Call returns on object describing to the middleware what to do. This is similar to action creators, which describe actions for the reducer.
|
|
|
+* For testing, you can just check whether the effect object ist correct, you don't need to actually call the async function.
|
|
|
+ - <https://redux-saga.github.io/redux-saga/docs/basics/DeclarativeEffects.html>
|
|
|
+
|
|
|
+## Dispathing actions to the store
|