Redux at Cobalt.io

Cameron Clifford
Sep 8, 2018 · 5 min read

For a redux tutorial check here and here. This post will focus on how we at Cobalt.io use redux!

Redux is a core building block of our React app. Our main purpose for using redux is to index and store data from our API. The most important piece of redux is the store. The store is essentially a large hash sitting in memory on the user’s browser containing the state of your app.

Let’s start by looking at the functions that define how your redux store handles state management: reducers. As their name implies they are useful for performing operations or even reducing the data that is received from external sources such as an API. Reducers take data in, process it, and return a version of that data that should be easily useable by your app. This step is helpful when calculations or indexing needs to be done. A quick overview of a simple reducer:

const pentestReducer = (state={}, action) => {
switch (action.type) {
case 'RECEIVE_PENTEST':
return {
...state,
[action.data.token]: { action.data }
};
default:
return state;
}
}
export default pentestReducer;

Each reducer is passed two variables, the state and the action. The state contains the current state for that node in the store. This means that when the app starts the pentestReducer function will return a default state of {}. Which will in turn set the store value to {}. When RECEIVE_PENTEST action is dispatched the reducer function will hit the case statement above and run the code there — in this case this node of the store is indexed by token. The state of the node looks like this after multiple RECEIVE_PENTEST actions are dispatched:

console.log(state.pentests);
{
pt_1: { slug: 'web-pentest-1', title: 'Web Pentest1', ...},
pt_2: { ... },
...
}

Maybe you’re thinking how does state have a pentests key? Now that we have an understanding of what a reducer is (more on that here) we can now jump into how the redux store is built to answer that question!

We use 4 functions from the redux module to help us build our store:

import { combineReducers, createStore, compose, applyMiddleware } from 'redux';

Our root reducer uses the combineReducers function from redux to compile all our reducers into one super reducer that I’ll call the rootReducer:

const rootReducer = combineReducers({
pentests: pentestReducer,
vulnerabilities: vulnerabilityReducer,
...
});

Then with our rootReducer we create the store using createStore:

export const store = createStore(rootReducer, undefined,                   
compose(applyMiddleware(...middleware))
);

createStore also accepts a preloaded state (undefined in the above example) as well as any desired middleware. You’ll also notice the compose and applyMiddleware functions in there.

Applying middleware is useful when you want to wrap the dispatch method in redux to perform async actions. As the authors mention the applyMiddleware function was included in the core of redux to standardize the way dispatch is extended and not necessarily because they think middleware is necessary. One middleware we find useful in development is the redux-logger which logs all the dispatched actions to the console.

Now that we have our redux store let’s look at how individual components can read from that store.

The most common library used for hooking components into a redux store is react-redux. There are two essential elements that facilitate communication between store and components. One is the <Provider/> component and the second is the connect method.

I’ll be brief on the <Provider/> component and just say that it allows the connect method to be used to wrap child components.

The connect method is the critical function that allows React components to dispatch actions and connect to the redux store. The connect function takes two arguments (both functions) commonly named mapStateToProps and mapDispatchToProps. Following our pentests example from above the mapStateToProps function looks like:

const mapStateToProps = (state, ownProps) => {
const token = ownProps.token
return {
pentest: state.pentests[token]
}
}

mapStateToProps does what it sounds like it might do! It maps the value or state from our redux store into the local component props. BAM! This gives the ability to pull state from your redux store into local component props — in this example this.props.pentest.

Often there is confusion between state and store. The store is the collection of all nodes defined in the rootReducer and the state is the current value of one of those nodes. React also has local component state which further complicates the matter :)

Then the mapDispatchToProps:

const mapDispatchToProps = (dispatch) => {
return {
sendRequest: (params) => dispatch(sendRequest(params))
}
}

mapDispatchToProps is react-redux‘s way of giving access to the dispatch function. Calling the dispatch function triggers redux to go through rootReducer looking for a case that meets the action.type that we pass to the dispatch function.

At Cobalt we use a wrapper around all of our requests called sendRequest that dispatches actions automatically. Then all we need to pass from individual components is:

pentestRequestArgs = (token) => {
return {
method: 'GET',
url: `/v1/pentest/${token}`,
dispatch: {
requestType: 'REQUEST_PENTEST',
responseType: 'RECEIVE_PENTEST'
}
}
}
this.props.sendRequest(pentestRequestArgs(token));

When the network request returns, sendRequest automatically dispatches the RECEIVE_PENTEST action, our reducer captures that action and updates our store which in turn updates our components. Connecting redux to react in this way is enormously powerful!

Summary

The entire process from the users perspective:

  • User clicks on a pentest
  • User is routed to a pentest show page dispatchingREQUEST_PENTEST
  • API responds to the request with the pentest data
  • RECEIVE_PENTEST action is dispatched and passes the response data to the reducer
  • Data from the response is indexed in the store
  • The change in store causes the mapStateToProps function defined in a component to be called
  • Redux has effectively passed store data down to a component
  • The user sees an updated view

If the user clicked on pentest pt_1 and the app goes through the above steps. We are left with pt_1 indexed in our store. If the user clicks on pt_2 the same steps are followed. When that user goes back to pt_1 the data is already in the store indexed by pt_1 and the user experience becomes an instant load of the page — anything that has changed between the first and second visit to the pt_1 page will be updated when the request returns and the action for updating the store is dispatched.

Closing Thoughts

Redux has changed the game for front end application state management. Even 5 years ago before the industry witnessed wide scale front end framework adoption, patterns like front end state management emerged. I remember serving pages from server side templating and holding a JavaScript object on the front end called pageState. pageState held all the important data from async calls similar to redux today. How far the JavaScript community has come!

Thanks to Mike Shema and Julie Kuhrt

Cameron Clifford

Written by

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade