Step to step guide to build a TODO app with React and Redux

10 Min. Read
Feb 28, 2020

 React Redux TODO Learn Tutorial ES6 Javascript

Introduction

You can get the source code here https://github.com/shivabhusal/todo-app-with-react-and-redux

React

React is spreading like a wildfire in Web Development Industry, as per StackOverflow survey 2019.


React Survey - Stackoverflow

React is used to build modern web UI in more manageable way. Moreover, I can say that now we can utilize our programming skills to build UIs. Back in the days, UI developers used to be the people with HTML / CSS / Bootstrap / Photoshop skill set. Those days, we didn’t used to call UI development a programming job.

Redux

Redux is tool to manage your application state more manageable and usable way you can imagine. Without it you would have to pass data as props deep down to the lower level components.

Basic Idea of Component Based Design

react-components-diagram

Lets start building a TODO app

We will use create-react-app tool to create a new app. It will configure all necessary tool-chain like babel, webpack and other.

1
npx create-react-app todo-app-with-redux

yes its npx, (npx is a package runner tool that comes with npm 5.2+ and higher, see instructions for older npm versions)

File Structures

1
2
3
4
5
6
7
8
9
src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
└── setupTests.js

Components

1
2
3
4
5
6
7
8
// src/components/visibilityFilter.jsx
import React from 'react'

export default function VisibilityFilter({ filters }) {
    return (
        filters.map((filter,i) => <button key={`filter-${i}`}>{filter}</button>)
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/components/todoList.jsx

import React from 'react'

const Todo = ({ todo }) => <li>{todo.content}</li>

export default function TodoList({ todos }) {
    return (
        todos.map((todo, i) => (
            <Todo key={i} todo={todo} />
        ))
    )

}
1
2
3
4
5
6
7
8
9
10
11
12
// src/components/addTodo.jsx

import React from 'react'

export default function addTodo() {
    return (
        <>
            <input type="text" placeholder="You text here" />
            <button>Add</button>
        </>
    )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/App.jsx

import React from 'react';
import './App.css';
import AddTodo from './components/addTodo'
import TodoList from './components/todoList'
import VisibilityFilter from './components/visibilityFilter'

const filters = ['all', 'completed', 'incomplete']
function App() {
  return (
    <div className="App">
      <h1>TODO Managers</h1>
      <AddTodo/>
      <TodoList todos={[{content: 'Task 1'}, {content: 'Task 2'}]}/>
      <VisibilityFilter filters={filters}/>
    </div>
  );
}

export default App;

Now, since UI of the app is ready its time to make it alive.

Introducing Redux

Now, we need to introduce Redux to feed data to the components and actions. We could have used React Context API but its more easier to use Redux and pass action around.

We need the following new components:-

  • Action Types : Types of all possible actions
  • Action Creators : Functions those take payload and prepare object with type and payload
  • Reducers : Functions that knows what to do with that action type.
  • Containers are created using connect() function which exposes relevant state and action function.
  • Store : Application state; created using createStore() function that takes, combined reducers as arguments.
  • Provider wraps the entire app and it exposes store to the app.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// src/redux/actions.js

import { ADD_TODO, TOGGLE_TODO, DELETE_TODO, SET_FILTER } from './actionTypes'

export const addTodo = (content) => (
    {
        type: ADD_TODO,
        payload: {
            content
        }
    }
)

export const toggleTodo = (id) => (
    {
        type: TOGGLE_TODO,
        payload: {
            id
        }
    }
)

export const deleteTodo = (id) => (
    {
        type: DELETE_TODO,
        payload: {
            id
        }
    }
)

export const setFilter = (filter) => (
    {
        type: SET_FILTER,
        payload: {
            filter
        }
    }
)


1
2
3
4
5
6
7
8
9
// src/App.css

button.active{
  background-color: red;
}

li.completed{
  text-decoration: line-through;
}
1
2
3
4
5
6
7
8
9
10
11
// src/redux/actionTypes.js

export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const SET_FILTER = 'SET_FILTER'

export const FILTER_ALL = 'all'
export const FILTER_COMPLETED = 'completed'
export const FILTER_INCOMPLETE = 'incomplete'
export const Filters = [FILTER_ALL, FILTER_COMPLETED, FILTER_INCOMPLETE]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// src/redux/reducers.js

import {FILTER_ALL} from './actionTypes'
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO, SET_FILTER } from './actionTypes'

const initialTodoState = {
    nextId: 2,
    data:
    {
        1: {
            content: 'Content 1',
            completed: false
        }
    }
}

export const todos = (state = initialTodoState, action) => {
    switch (action.type) {
        case ADD_TODO: {
            return (
                {
                    ...state,
                    data: {
                        ...state.data,
                        [state.nextId]: {
                            completed: false,
                            content: action.payload.content
                        },
                    },

                    nextId: state.nextId + 1
                }
            )
        }
        case TOGGLE_TODO:{
            console.log(action.payload)
            return(
                {
                    ...state,
                    data:{
                        ...state.data,
                        [action.payload.id]:{
                            ...state.data[action.payload.id],
                            completed: !(state.data[action.payload.id].completed)
                        }
                    }
                }
            )
        }

        default: {
            return state
        }
    }
}


export const visibilityFilter = (state = {activeFilter: FILTER_ALL}, action) => {
    switch (action.type) {
        case SET_FILTER: {
            return ({
                activeFilter: action.payload.filter
            })
        }

        default: {
            return state;
        }
    }
}
1
2
3
4
5
6
7
// src/redux/store.js

import {createStore, combineReducers} from 'redux'
import {todos, visibilityFilter} from './reducers'

export default createStore(combineReducers({todos, visibilityFilter}), 
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// src/App.jsx

import React from 'react';
import './App.css';
import AddTodo from './components/addTodo'
import TodoList from './components/todoList'
import VisibilityFilter from './components/visibilityFilter'
import {Provider} from 'react-redux'
import store from './redux/store'

function App() {
  return (
    <Provider store={store}>
    <div className="App">
      <h1>TODO Managers</h1>
      <AddTodo/>
      <TodoList/>
      <VisibilityFilter/>
    </div>
    </Provider>
  );
}

export default App;


Updating the Ui Components to connect with Store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/components/addTodo.jsx

import React, { useState } from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'

function AddTodo({ addTodo }) {
    const [value, setValue] = useState('');

    const handleOnChange = (e) => {
        setValue(e.target.value)
    }
    const handleAdd = () => {
        setValue('')
        addTodo(value)
    }

    return (
        <>
            <input type="text" onChange={handleOnChange} value={value} placeholder="You text here" />
            <button onClick={handleAdd}>Add</button>
        </>
    )
}

export default connect(null, { addTodo })(AddTodo)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// src/components/todoList.jsx

import React from 'react'
import { connect } from 'react-redux'
import { _ } from 'underscore'
import { FILTER_ALL, FILTER_COMPLETED } from '../redux/actionTypes'
import { toggleTodo } from '../redux/actions'

const Todo = ({ todo, id, toggleTodo }) => (
    <li className={todo.completed ? 'completed' : ''} onClick={() => toggleTodo(id)}>{todo.content}</li>
)

function TodoList({ todos, toggleTodo }) {
    return (
        _.keys(todos).map((id) => (
            <Todo key={id} id={id} toggleTodo={toggleTodo} todo={todos[id]} />
        ))
    )
}

const mapState = (state) => {
    if (state.visibilityFilter.activeFilter === FILTER_ALL) {
        return { todos: state.todos.data }
    } else if (state.visibilityFilter.activeFilter === FILTER_COMPLETED) {
        return ({
            todos: _.pick(state.todos.data, (todo) => todo.completed)
        })
    } else {
        return ({
            todos: _.pick(state.todos.data, (todo) => !todo.completed)
        })
    }
}

export default connect(mapState, { toggleTodo })(TodoList);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// src/components/visibilityFilter.jsx

import React from 'react'
import { connect } from 'react-redux'
import { setFilter } from '../redux/actions'
import { Filters } from '../redux/actionTypes'

function VisibilityFilter({ activeFilter, setFilter }) {
    return (
        Filters.map((filter, i) => (
            <button
                className={filter === activeFilter ? 'active' : ''}
                onClick={() => setFilter(filter)}
                key={`filter-${i}`}>
                {filter}
            </button>
        ))
    )
}

const mapState = (state) => ({ activeFilter: state.visibilityFilter.activeFilter })
export default connect(mapState, { setFilter })(VisibilityFilter)

You might also like

The most easiest way to learn React and more easier way to learn how to use Redux with React
Read More..

How To Apply For Adding Category To Driving License In Nepal Online 

ShivaShiva Bhusal (Software Engineer) 0 Min. Read Feb 16, 2020

Its easy to apply for adding category driving license online here in Nepal. Just goto the link mentioned with mentioned document on time.
Read More..

Ruby Object Model 

ShivaShiva Bhusal (Software Engineer) 2 Min. Read Dec 28, 2019
 Ruby Ruby Object Model Objects OOP

Object is the base of the idea of Object Oriented Programming. Object is the mapping of real world objects or resources in to the digital paradigm.
Read More..


Configure Rails Admin With I18n 

ShivaShiva Bhusal (Software Engineer) 1 Min. Read Aug 8, 2017
 Ruby On Rails Ruby On Rails Admin Configure I18n

By default, its not that easy to configure your application with i18n. If you wish to support your local language like `esp`, `np`, `hi`, `mx`, etc. you need to follow the steps i tell you.
Read More..


Write To Me

Hire me shiva bhusal
We'll never share your email with anyone else.
I'll never share your email with anyone else.