This document summarizes the evolution of Redux action creators from plain objects to functions that return action objects, and then to functions that return functions that dispatch actions. It discusses how middleware like Redux-Thunk and dependency injection allow async logic and testing of action creators. Redux-Saga is introduced as providing a cleaner way to write complex asynchronous action creators using generator functions. Key benefits of Redux-Saga include easy testing and ability to create daemon processes using patterns like takeLatest and takeEvery.
21. Tests are still complecated
We have some mess in components
etc...
function() {
updateSearchPage()({dispatch, getState: buildState(), api, cookie});
expect(dispatch.calledOnce).to.be.true;
expect(calledWithActions(
dispatch.getCall(0).args,
APPLIED_FILTERS_CHANGED,
GET_NOTICES_SUCCESS
)).to.be.true;
};
function onHandlePress () {
this.props.dispatch({type: 'SHOW_WAITING_MODAL'})
this.props.dispatch(createRequest())
}
REDUX-SAGA
22. The most elegant way to write complecated action creators
Look at this beautiful code
export function* authFlow() {
while(true) {
yield take(USER_AUTH_CHECK);
yield fork(authenticate);
const {user, token} = yield take(USER_AUTH_SUCCESS);
Session.save(user, auth);
yield put(redirectTo('/'));
const action = yield take(USER_SIGN_OUT);
Session.clear();
yield put(redirectTo('/'));
}
}
25. Generators are Functions with bene ts.
function* idMaker(){
var index = 0;
while(true)
yield index++;
}
var gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
26. co - generator based control ow
var fn = co.wrap(function* (val) {
return yield Promise.resolve(val);
});
fn(true).then(function (val) {
});
28. Simple example What happens
here?
select part of the state
call the api method
put an action
export function* checkout() {
try {
const cart = yield select(getCart);
yield call(api.buyProducts, cart);
yield put(actions.checkoutSuccess(cart));
} catch(error) {
yield put(actions.checkoutFailure(error));
}
}
29. Easy to test
test('checkout Saga test', function (t) {
const generator = checkout()
let next = generator.next()
t.deepEqual(next.value, select(getCart),
"must select getCart"
)
next = generator.next(cart)
t.deepEqual(next.value, call(api.buyProducts, cart),
"must call api.buyProducts(cart)"
)
next = generator.next()
t.deepEqual(next.value, put(actions.checkoutSuccess(cart)),
"must yield actions.checkoutSuccess(cart)"
)
t.end()
})
34. can be useful to handle AJAX requests where we
want to only have the response to the latest request.
takeLatest
function* takeLatest(pattern, saga, ...args) {
let lastTask
while(true) {
const action = yield take(pattern)
if(lastTask)
// cancel is no-op if the task has alerady terminated
yield cancel(lastTask)
lastTask = yield fork(saga, ...args.concat(action))
}
}
35. allows multiple saga tasks to be forked
concurrently.
takeEvery
function* takeEvery(pattern, saga, ...args) {
while (true) {
const action = yield take(pattern)
yield fork(saga, ...args.concat(action))
}
}