Blog Articles Banner
Development

Building Add activity demo app using React, Redux & ImmutableJS



A typical reactJS based application is usually composed of several UI components that share data. Multiple components are tasked with the responsibility of displaying different properties of the same object. The object represents state which can change at any time. To keep our code simple, we’d need to manage our state elsewhere.


In this blog we’ll pair React with two libraries and use them to build the Add activity app.


Redux


Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server and native), and are easy to test. On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.


At the core of a Redux store is a function that takes the current application state and an action and combines them to create a new application state. We call this fun a reducer.


Our React components will be responsible for sending actions to our store, and in turn our store will tell the components when they need to re-render.


ImmutableJS


Because Redux doesn’t allow us to mutate the application state, it can be helpful to enforce this by modeling application state with immutable data structures.


Demo


We’re going to use React with Redux and ImmutableJS to build Add activity app that allows us to add new todo activities and toggle them between complete and incomplete statuses.


The code is available in this repository on GitHub.


Deployment and running the application


The install all the dependencies and run this demo application application we need to run npm install and after this run npm start.
We’ll also need to run npm run build each time we want to compile the code.


React and Components


For this application, we need two React components, <Todo /> and <ActivityList />


// src/component.js
import React from 'react';
// Todo component
export function Todo(props) {
const { todo } = props;
if(todo.isDone) {
return <strike>{todo.text}</strike>;
} else {
return <span>{todo.text}</span>;
}
}
// ActivityList component
export function ActivityList(props) {
const { todos, toggleTodo, addTodo } = props;
const onSubmit = (event) => {
const input = event.target;
const text = input.value;
const isEnterKey = (event.which == 13);
const isLongEnough = text.length > 0;
if(isEnterKey && isLongEnough) {
input.value = '';
addTodo(text);
}
};
const toggleClick = id => event => toggleTodo(id);
return (
<div className='todo'>
<header className="header"><h2>My activity list</h2></header>
<input type='text'
className='todo__entry'
placeholder='Add new activity'
onKeyDown={onSubmit} />
<ul className='todo__list'>
{todos.map(t => (
<li key={t.get('id')}
className='todo__item'
onClick={toggleClick(t.get('id'))}>
<Todo todo={t.toJS()} />
</li>
))}
</ul>
</div>
);
}


The two components can be tested by creating an index.html file and populating it with the following markup.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>


We will add src/app.js that serves as the entry point for this application.
// src/app.js
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer';
import { ActivityList } from './containers';
const store = createStore(reducer);
render(
<Provider store={store}>
<ActivityList />
</Provider>,
document.getElementById('app')
);


Compile the code with npm run build, then run npm run start to start this application in a browser.


React and Immutable


After creating the user interface, we can start to think of the state behind it. We’ll learn this using some dummy data  that we can easily translate it into immutableJS collections:


import { List, Map } from 'immutable';


const dummyTodos = List([
Map({ id: 0, isDone: true,  text: 'select a topic for writing blog' }),
Map({ id: 1, isDone: false, text: 'blog completed' })
]);


To make the immutableJS work we have to make some slight tweaks to our components. Anywhere there was a property access before (e.g. todo.isDone) needs to become a method call instead (todo.get(‘isDone’))


Actions


In this application we’ll only need two actions, one to add a new todo and the other to toggle an existing one. Each action is a JavaScript object with type and payload properties. The type property helps us decide what to do with the payload when we process the action.


// src/action.js
const uid = () => Math.random().toString(34).slice(2);


export function addTodo(text) {
return {
  type: 'ADD_TODO',
  payload: {
    id: uid(),
    isDone: false,
    text: text
  }
};
}


export function toggleTodo(id) {
return {
  type: 'TOGGLE_TODO',
  payload: id
};
}


Reducer


The reducer is a function that takes a state and an action, then uses them to compute a new state. As we have created our state and the actions that update it, we can build our reducer.


Here the code:


// src/reducer.js
import { List, Map } from 'immutable';


const init = List([]);


export default function reducer(todos=init, action) {
switch(action.type) {
  case 'ADD_TODO':
    return todos.push(Map(action.payload));
  case 'TOGGLE_TODO':
    return todos.map(t => {
      if(t.get('id') === action.payload) {
        return t.update('isDone', isDone => !isDone);
      } else {
        return t;
      }
    });
  default:
    return todos;
}
}


Connecting Everything


We’ve got our actions and reducer ready, we can create a store and connect it to our React components.


// src/app.js
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducer';
import { ActivityList } from './containers';


const store = createStore(reducer);


render(
<Provider store={store}>
  <ActivityList />
</Provider>,
document.getElementById('app')
);


Using react-redux we will create store-aware containers that wrap around our components.


We create a container with the connect function. When we call connect(), we pass two functions mapStateToProps() and mapDispatchToProps()


// src/containers.js
function mapStateToProps(state) {
  return { todos: state };
}


To dispatch the actions from our action creators, we need to supply a mapDispatchToProps function..


function mapDispatchToProps(dispatch) {
  return {
    addTodo: text => dispatch(addTodo(text)),
    toggleTodo: id => dispatch(toggleTodo(id))
  };
}


Now that we’ve mapped our component to the action creators, we can call them from event listeners:


export function ActivityList(props) {
const { todos, toggleTodo, addTodo } = props;


const onSubmit = (event) => {
  const input = event.target;
  const text = input.value;
  const isEnterKey = (event.which == 13);
  const isLongEnough = text.length > 0;


  if(isEnterKey && isLongEnough) {
    input.value = '';
    addTodo(text);
  }
};


const toggleClick = id => event => toggleTodo(id);


return (
  <div className='todo'>
    <header className="header"><h2>My activity list</h2></header>
    <input type='text'
           className='todo__entry'
           placeholder='Add new activity'
           onKeyDown={onSubmit} />
    <ul className='todo__list'>
      {todos.map(t => (
        <li key={t.get('id')}
            className='todo__item'
            onClick={toggleClick(t.get('id'))}>
          <Todo todo={t.toJS()} />
        </li>
      ))}
    </ul>
  </div>
);
}


The container will re-render the wrapped component whenever their mapped props change.


We need to make the containers aware of the store, using the <Provider /> component.


const store = createStore(reducer);
render(
<Provider store={store}>
  <ActivityList />
</Provider>,
document.getElementById('app')
);


Summary


I hope this blog has given you a useful introduction to create a reactJS application using redux and immutableJS. For any questions or to provide feedback please visit: http://apps.topcoder.com/forums/?module=Thread&threadID=925407&start=0&mc=1#2296982