A single data store called Redux

-

Redux is a data store container for JavaScript apps. I’m currently on a team, that’s building an application for the government in three months. We use React and Redux in the front-end. This combination works well for us. In this blog I’ll explain a little bit about what we have done so far and how we implemented Redux.

We’re building a Content Management System (CMS) for editors (users) to enter information and later disclose it through an API. In order to show and edit the data in a clear way for the users in the front-end, we want to have the display of the data (rendered UI components) linked to the data itself. The displayed data should be re-rendered when the data changes. Redux controlled React components come in handy for this! We store the data retrieved from the server through asynchronous calls in the data store container of Redux. When the user uses the user interface by going to different pages, tabs or saving forms, a call is triggered to fetch the latest data from the server. This data (partially) overwrites the old data in the store and brings the store to a new state. When data is changed in the store the parts (components) of the user interface that display the data are re-rendered. The actions of the user trigger (actually dispatch) Redux Actions. Responses from the server also dispatch Redux Actions. These Actions contain a type and a payload. The type specifies which kind of action is triggered. The payload contains the new data, which can be the response (body) of the server. This data needs to be put in the data store. This is done by a Reducer. The Reducer is responsible of changing the state of the store. Actually it takes (part of) the previous state and switches over the type of Action, manipulates the data from it accordingly and returns the new state. It uses the payload of the Action to create a new state. It can do anything more you want too, like sorting arrays or execute other functions on the state. This way you have a time line of the state and its changes due to Actions.

 

// actions.js export const createResource = formData => ({ type: 'CREATE_RESOURCE_REQUEST_ACTION', payload: formData });

export const createResourceSuccess = response => ({ type: ‘CREATE_RESOURCE_RESPONSE_SUCCESS_ACTION’, payload: response });

export const createResourceFailed = err => ({ type: ‘CREATE_RESOURCE_RESPONSE_FAILED_ACTION’, payload: err });

 

// reducer.js const initialState = { isFetching: false, createdResources: [], };

export const reducer = (state = initialState, action) => { switch (action.type) { case ‘CREATE_RESOURCE_REQUEST_ACTION’: return { …state, // copies the previous state isFetching: true // overwrites this field in the copied state }; case ‘CREATE_RESOURCE_RESPONSE_SUCCESS_ACTION’: return { isFetching: false, createdResources: […state.createdResources, action.payload] }; default: return state; } };

In a complex, big application all these can be split, for example by a feature. So you can make Actions, a Reducer and React components for a particular feature. This way you can easily order and maintain your application. There are also Redux DevTools and extensions for multiple browsers to look into the data store and debug while you code. In our application we use Saga’s. When a user triggers an Action that needs to make a request to the server this is handled by a worker Saga. The worker Saga sends an asynchronous request to the server. Depending on the response of the server, i.e. a success or a failure, the Saga dispatches a new Action specifically for a success or failure response of the specific request. Then the Reducer catches this to handle the data from the response.

 

// saga.js import { call, put, takeLatest } from 'redux-saga/effects'

export function* createResource(action) { const { formData } = action.payload; try { const response = yield call(axios.post, https://amsterdam.luminis.eu/api/resources, formData); yield put(actions.createResourceSuccess(response.data)); } catch (e) { yield put(actions.createresourceFailed(e)); } }

export function* saga() { yield takeLatest(‘CREATE_RESOURCE_REQUEST_ACTION’, createResource); }