Microblog React Project
By: Cristian Salcescu
Tags: JavaScript, Functional Programming, React, Redux, Front End Development

Introduction

When building the microblog application we are going to follow the core functional principles of using pure functions and immutable data.

Immutable objects cannot be changed after creation. Pure functions compute the same result given the same input and have no side effects. Any interaction with the environment outside the function like changing an external variable, reading, or writing to the HTML DOM, making network calls is a side-effect.

Unidirectional Data Flow

It turns out that the best way to achieve this purity of functions is to put them inside a data flow.

Here is a sketch of the unidirectional data flow we are going to follow.

The state defines all the application data that may be displayed on the screen and can change.

The component is a function transforming part of the state data into a visual interface. The visual interface is made of HTML and CSS.

Components define also the actions that are to be dispatched when the user interacts with the UI. I am going to use the word “actions” for synchronous actions and the term “effects” for asynchronous actions.

The plain actions are just data transfer objects describing how the state should change.

The effects are more complex, and we will discuss them in a concrete example. These effects encapsulate impure code and are a compromise we have to do when starting a practical project because there is no popular option for building an application using only pure functions.

The reducer takes the existing state and an action and returns a new state object.

This endless flow where the data is initially stored then displayed on the screen and after that changed based on the user interaction stays at the base of building the application.

Let’s examine all these parts on the trivial counter example.

Start by installing the Redux store.

npm install redux react-redux --save

State

In this case, the state is just a number initialized with 0 .

const initialState = 0;

Action Types

We need two action types, one for incrementing the number and the other for decrementing it.

const INCREMENT = 'INCREMENT';

const DECREMENT = 'DECREMENT';



export { INCREMENT, DECREMENT };

Action Creators

The action creators’ functions use the previous types to create plain action objects with a type property.

import { INCREMENT, DECREMENT } from './action-types';



function increment(){

return {

type: INCREMENT

}

}



function decrement(){

return {

type: DECREMENT

}

}



export {

increment,

decrement

}

Reducer

The reducer function increments the count when the INCREMENT action is received and decrements it when the DECREMENT action is dispatched.

import { INCREMENT, DECREMENT } from './action-types';



function reducer(counter = initialState, action){

switch(action.type){

case INCREMENT:

return counter + 1;

case DECREMENT:

return counter - 1;

default:

return counter;

}

}



export default reducer;

Component

The component gets in the counter number and two callbacks, increment , and decrement . The callbacks are used as click event handlers.

import React from 'react';



function Counter({

counter, increment, decrement }){

return(

<div>

<div>{counter}</div>

<div>

<button

type='button'

onClick={decrement}>

-

</button>

<button

type='button'

onClick={increment}>

+

</button>

</div>

</div>

)

}

The Counter component connects to the store to read the counter state and dispatch the increment and decrement actions.

import React from 'react';

import { connect } from 'react-redux';



import { increment, decrement } from './action-creators';



function Counter({

counter, increment, decrement}){}



export default connect(

counter => ({ counter }),

{increment, decrement}

)(Counter);

Action Creator Helper

The creation and usage of actions become repetitive. We can simplify their creation using a helper. The Redux Actions library offers such a utility function.

npm install redux-actions --save

Here is how we can build the action creators using a helper.

import { createAction } from 'redux-actions';



const increment = createAction('Increment');

const decrement = createAction('Decrement');



export default {

increment,

decrement

}

The createAction helper takes a type and returns the action creator function. It first defines a function that takes a payload data object and returns a plain object containing the type and the payload .

Reducer Helper

The creation of the reducer function can be also simplified.

import { handleActions } from 'redux-actions';

import actions from './actions';



const initialState = 0;



function increment(state, action){

return state + 1;

}



function decrement(state, action){

return state + 1;

}



export default handleActions({

[actions.increment]: increment,

[actions.decrement]: decrement

}, initialState);

There are a few advantages to this approach.

First, the code will still be cleaner when we add more logic as the mapping object contains just the matching between the action and the smaller reducer function it has to call.

There is no need to keep a distinct piece of information regarding an action: the action creator and the action type. We can map directly the action creator to a smaller updater function.

Second, it forces us to extract out the update logic and give it an expressive name. This makes it clear that the role of the reducer is to handle actions by updating the state.

The handleActions utility that helps us mapping the action to the smaller reducer function may look like the one below.

function handleActions(actionMap, initialState){

return function(state = initialState, action){

const update = actionMap[action.type]; if(typeof(update) === 'function'){

return update(state, action);

} else {

return state;

}

}

}

The actionMap is a map between the action type and the reducer function we have to execute for it.

Redux Toolkit

Redux Toolkit is the recommended approach for working with the Redux store. It includes utilities that simplify common tasks, including the ones discussed before, createAction and createReducer.

npm install @reduxjs/toolkit --save

Here is how we can use the createReducer helper in a similar way to handleActions to simplify the creation of the reducer functions by providing an object mapping actions to smaller reducer functions.

When the specific action is dispatched the associated reducer will update the state.

import { createReducer } from '@reduxjs/toolkit'; //... export default createReducer(initialState, {

[actions.increment]: increment,

[actions.decrement]: decrement

});

From now on we are going to use the Redux Toolkit to simplify the action creation and the mapping of actions to smaller reducers.

Knowing all these now look at the requirements and start building the Microblog React Project.