Getting the Team Up to Speed With Our Front-End Architecture Part 3: Keeping the Reducer Pure

Reducers should be pure functions. What this means is that there should be no side effects. Now, what this really means is return values are only determined by input values.

When using redux API calls are common place inside of reducer to handle sending/receiving data. However, this ruins the idea of keeping our reducer pure because there can be network errors that produce side effects. This is why we use Redux Saga. From their website “redux-saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.”

Redux Saga simply hooks into the store. It reads the actions from it to make it’s own API calls. Once those calls return, it then creates actions and passes on the information to the store again via an action.

The first thing we have to do is install redux saga via

npm install redux-saga

Once it has been installed you need to open up your store and add a reference to it

import createSagaMiddleware from 'redux-saga'

Then we need to update our store to add the saga middleware into it

const sagaMiddleware = createSagaMiddleware()
const store = createStore(rootReducer(history), initialState, applyMiddleware(logger, routerMw, sagaMiddleware))
sagaMiddleware.run(mySaga)

The mySaga above hasn’t been created yet as we haven’t yet created the sagas that we want to be able to run.

import { 
    put, 
    takeEvery 
} from 'redux-saga/effects'

import {
   SHOW_MESSAGE
} from './reducers/applicationSettingsReducer'

import {
    ICharacterResponse
} from './types'

import {
    GetCharacters,
} from './api'

import { GetCharactersBeginAction, GET_CHARACTERS_BEGIN, GET_CHARACTERS_SUCCESS } from './reducers/characterReducer'

function* getCharacters({payload: undefined} : GetCharactersBeginAction) {
    try {       
        const characterResponse : ICharacterResponse = yield GetCharacters()
    
        yield put({ type: GET_CHARACTERS_SUCCESS, payload: characterResponse })
    }
    catch (e) {
        yield put({
            type: SHOW_MESSAGE, 
            payload: {
                messageType: 'error',
                open: true, message:
                    `Error Loading Action Details ${e}`
            }
        })
    }
}


function* mySaga() {
    yield takeEvery(GET_CHARACTERS_BEGIN, getCharacters)   
}

export default mySaga;

And we have to add the actual API call

export async function GetCharacters() {
    const url = BASE_URL + `character`

    const response = await fetch.get<ICharacterResponse>(url)
        .then(res => { return res })

    return response
}

That’s all there is to it. Take a look at the full source at the url https://bitbucket.org/AndySattler/react-starter/src/master/

Questions, Comments, Clarifications – let me know and thanks for reading

2 Comments

  1. You have made some decent points there. I looked on the net for more info about the issue and found most people will go along with your views on this website. Arlee Cobbie Annaliese

Comments are closed.