Ema Suriano
Software Engineer during the day, cook at night 👨🍳 Traveller and Foodie 🧳 Whenever I can I like to write and speak 🤓 Berlin 📍
It provides a third-party extension point between dispatching an action, and the moment it reaches the reducer.
Allow us to add code that will run before action is passed to the reducer.
const myMiddleware = ({ getState, dispatch }) => next => action => {
/*
before reducers have run
*/
next(action); // will propagate the action down the middleware chain
/*
after reducers have run
*/
};
Will pass the action alogn the middleware chain (from the current) down to the reducer.
Will start the action flow from the beggining (the first middleware in the), so it will eventualyy reach the current one as well.
next()
dispatch()
action
Middleware Chain
import { createStore, applyMiddleware } from 'redux';
import reducer from 'reducers/root';
import thunk from 'redux-thunk';
import measureMiddleware from 'middleware/measure';
const middlewareChain = applyMiddleware(measureMiddleware, thunk);
const store = createStore(reducer, middlewareChain);
By calling applyMiddleware and passing our middlewares, we are defining the middleware chain for the application. The order is defined by the middleware's order.
const middleware = [apiMiddleware]; //middlewares that will run everywhere
if (development) {
middleware.push(measureMiddleware); // only on dev
} else {
middleware.push(analyticsMiddleware);
}
const store = createStore(reducer, applyMiddleware(...middleware));
We have the ability to defined which middlewares will run inside our differents enviroment. For example, we want to use the measureMiddleware only in Dev.
const measureMiddleware = () => next => action => {
console.time(action.type);
next(action);
console.timeEnd(action.type);
};
export default measureMiddleware;
This middleware allow us to potentially catch poorly performing reducer implementations. We start the timing before running an action, then we tell the browser to print the timing after the action is done.
Our goal is to create a generic middleware that can serve any API request and requires only the information passed to it in an action.
To use our middleware we need to craft a special action object for it:
In this case, this middleware will manage any API request and requires only the information passed to it in an action.
To use our middleware we need to craft some special actions object for it:
const fetchUser = (id, cancelableId) => ({
type: API,
url: `user/${id}`,
next: SET_USER,
cancelableId,
});
const cancelAPI = (id) => ({
type: CANCEL_API,
id,
});
With this approach we can use the sme action type to handle three cases for async actions
const asyncActionType = (type) => ({
PENDING: `${type}_PENDING`,
SUCCESS: `${type}_SUCCESS`,
ERROR: `${type}_ERROR`,
});
export const LOGIN = asyncActionType('LOGIN');
export const FETCH_RECIPES = asyncActionType('FETCH_RECIPES');
const canceled = {};
const apiMiddleware = ({ dispatch }) => (next) => (action) => {
const handleResponse = (data) => {
if (action.cancelable && canceled[action.cancelable]) {
return;
}
dispatch({ type: action.payload.next.SUCCESS, payload: data })
};
if (action.type === API_REQUEST) {
fetch(action.payload.url)
.then((response) => response.json())
.then(handleResponse);
dispatch({ type: action.payload.next.PENDING });
}
if (action.type === CANCEL_API_REQUEST) {
canceled[action.id] = true;
setTimeout(() => delete canceled[action.id], 5000);
}
return next(action);
};
const MY_ACTION = {
type: 'MY_ACTION',
};
const doSomething = (dispatch) => {
dispatch(DO_SOMETHING);
dispatch(MY_ACTION);
}
const doMoreStuff = (dispatch) => {
dispatch(DO_MORE_STUFF);
dispatch(MY_ACTION);
}
const doWhateverYouLike= (dispatch) => {
dispatch(DO_WHATEVER_YOU_LIKE);
dispatch(MY_ACTION);
}
In this case, we're going to create a Middleware to help us managing side-effects on out application.
Let's suppose that we have an action (MY_ACTION) that needs to be dispatch after a series of actions.
const MY_ACTION = {
type: 'MY_ACTION',
};
const doSomething = (dispatch) => {
dispatch(DO_SOMETHING);
}
const doMoreStuff = (dispatch) => {
dispatch(DO_MORE_STUFF);
}
const doWhateverYouLike= (dispatch) => {
dispatch(DO_WHATEVER_YOU_LIKE);
}
const customMiddleware = ({ getState, dispatch }) => next => action => {
switch(action.type) {
case DO_SOMETHING:
case DO_MORE_STUFF:
case DO_WHATEVER_YOU_LIKE:
dispatch(MY_ACTION);
default: break;
}
next(action);
};
function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
Allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met.
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
function* mySaga() {
yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
export default mySaga;
Aims to make side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) in React/Redux applications easier and better.
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
// then run the saga
sagaMiddleware.run(mySaga)
// render the application
By Ema Suriano
Introduction to middlewares in Redux, and some examples of them created by the community.
Software Engineer during the day, cook at night 👨🍳 Traveller and Foodie 🧳 Whenever I can I like to write and speak 🤓 Berlin 📍