Step to step guide to build a TODO app with React and Redux
Tags: react, redux, tutorial, javascript
Published: 2020-03-02 01:42:25


## Introduction
You can get the source code here [https://github.com/shivabhusal/todo-app-with-react-and-redux](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](https://insights.stackoverflow.com/survey/2019#technology-_-web-frameworks).

---
![React Survey - Stackoverflow](https://shivab.com/images/assets/react-survey.png)

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](https://shivab.com/images/assets/react-components-diagram.jpg)

## 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.

```bash
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
```
src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
└── setupTests.js
```

## Components
```jsx
// src/components/visibilityFilter.jsx
import React from 'react'

export default function VisibilityFilter({ filters }) {
    return (
        filters.map((filter,i) => <button key={`filter-${i}`}>{filter}</button>)
    )
}
```

```jsx
// 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} />
        ))
    )

}
```

```jsx
// src/components/addTodo.jsx

import React from 'react'

export default function addTodo() {
    return (
        <>
            <input type="text" placeholder="You text here" />
            <button>Add</button>
        </>
    )
}
```

```jsx
// 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.



```js
// 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
        }
    }
)


```


```css
// src/App.css

button.active{
  background-color: red;
}

li.completed{
  text-decoration: line-through;
}
```

```js
// 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]
```

```js
// 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;
        }
    }
}
```

```js
// 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__())
```

```jsx
// 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

```jsx
// 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)

```

```jsx
// 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);
```

```jsx
// 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)
```

Read more from me:- [https://shivab.com/blog/](https://shivab.com/blog/)