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