React Redux Todo List

A simple todo list application using react and redux

Smile2gether
5 min readMar 22, 2018

If you understand the basic concept in React and Redux. All you need to focus are Action, Reducers, Store, and Components. In this project is about creating a React and Redux Todo List app creating your own server path and using webpack hot reload to make it real time updated!

For the file structure, action, reducer, and store will be kept in folder name redux. In the same way as before, all component like App (entry point), TodoInput (input text), TodoItems (a todo), and TodoList (all todos) in components, client, server, and config file.

react-todo-list
|– client
|–– client.js
|–– index.html
|– components
|–– App.js
|–– TodoInput.js
|–– TodoItems.js
|–– TodoList.js
|– redux
|–– actions.js
|–– reducer.js
|–– store.js
|- server
|–– server.js
|– package.json
|– webpack.config.js

In package.json will keep all project information about the dependencies, description. as well as the script.

Start from the scratch, we create a new folder name react-todo-list, go into the folder then initialize with npm.

$  mkdir react-todo-list
$ cd react-todo-list
$ npm init

Accept the default, maybe add some description or author name.

To install the webpack and dependencies to package.json

$  npm install webpack --save

Ps.
i = install
-S =--save

$  npm i -S react react-dom react-redux 
$ npm i -S redux redux-logger redux-storage
$ npm i -S babel-loader
$ npm i -S webpack-hot-middleware
$ npm i -S babel-loader babel-preset-es2015 babel-preset-react
$ npm i -S express webpack-dev-middleware

Name “serve” in this example is refers to the server files using with nodemon — reload, automatically.

{  "name": "react-todo-list",  "version": "1.0.0",  "description": "Todo List App",  "license": "MIT",  "author": "____YOUR_NAME____",  "scripts": {    "serve": "nodemon server/server.js --ignore components"  },}

webpack.config.js

var webpack = require('webpack');  module.exports = {    devtool: 'inline-source-map',    entry: [      'webpack-hot-middleware/client',      './client/client.js'    ],    output: {    path: require("path").resolve("./dist"),    filename: 'bundle.js',    publicPath: '/'  },  plugins: [    new webpack.optimize.OccurrenceOrderPlugin(),    new webpack.HotModuleReplacementPlugin(),    new webpack.NoEmitOnErrorsPlugin()  ],  module: {    loaders: [    {      test: /\.js$/,      loader: 'babel-loader',      exclude: /node_modules/,      query: {        presets: ['react', 'es2015']      }    }    ]  }}

Then first thing is to create a reducer with a default that returns the state.

redux/reducer.js

let reducer = (state, action) => {  switch (action.type) {    default:      return state;  }}export default reducer

Following by a store that collects the todo like the following code.

redux/store.js

import { applyMiddleware, compose, createStore } from 'redux'
import reducer from './reducer'
import logger from 'redux-logger'
let finalCreateStore = compose( applyMiddleware(logger()))(createStore)export default function configureStore(initialState = { todos: [] }) { return finalCreateStore(reducer, initialState)}

redux/actions.js

let actions = {}export default actions

Next, creating an HTML web page for todo list application, then create an entry point that connects React with Redux.

client/index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Todo List</title>
</head>
<body>
<div id="app"></div>
<script src="bundle.js"></script>
</body>
</html>

To this point create an entry point that connects React with Redux.

client/client.js

import React from 'react'
import { render } from 'react-dom'
import App from '../components/App'
import configureStore from '../redux/store'
import { Provider } from 'react-redux'
let initialState = {
todos: []
}
let store = configureStore(initialState)render( <Provider store={store}> <App /> </Provider>,document.getElementById('app'))

Then create a server config file that call HTML page on the setup port.

server/server.js

var express = require('express');
var path = require('path');
var config = require('../webpack.config.js');
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var app = express();
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));app.use(webpackHotMiddleware(compiler));app.use(express.static('./dist'));app.use('/', function (req, res) { res.sendFile(path.resolve('client/index.html'));});# ---- You can set up port right here ----
var port = 8000;
app.listen(port, function(error) { if (error) throw error; console.log("Express server listening on port", port);});

Build up all components by binding the data.

components/TodoInput.js

import React, { Component } from 'react'class TodoInput extends Component {  constructor(props) {    super(props)    this.state = {      title: ''    }  }  handleTitleChange(event) {    console.log(event);    this.setState({      title: event.target.value    })  }  handleSubmit(event) {    event.preventDefault()    if (this.state.title != '')    {      this.props.addTodo(this.state.title)      //reset input box      this.setState({        title: ''      })    }  }  render() {    return (    <div className="todo__input">
<div className="title">
<input
type="text"
placeholder="title.."
required={true}
value={this.state.title}
onChange={this.handleTitleChange.bind(this)}
/>
</div>
<div className="submitButton">
<button onClick={this.handleSubmit.bind(this)}>Add</button>
</div>
</div>
) }}export default TodoInput

components/TodoItem.js

import React, { Component } from 'react'class TodoItem extends Component {  constructor(props) {    super(props)    this.state = {       title: this.props.todo.title,      completed: this.props.todo.completed,    }  }  handleComplete() {    this.props.actions.completeTodo(this.props.todo.id)    this.setState({      checked: !this.state.checked    })  }  handleDelete() {    this.props.actions.deleteTodo(this.props.todo.id)  }  handleEdit() {    this.props.actions.editTodo(this.props.todo.id)  }  handleUpdate() {    if (this.state.title != '') {      this.props.actions.updateTodo(this.props.todo.id,        
this.state.title)
} } handleTitleChange(event) { this.setState({ title: event.target.value }) } handleDescriptionChange(event) { this.setState({ description: event.target.value }) } handleDueDateChange(event) { console.log(event); this.setState({ dueDate: event }) } handleFocusChange(event) { console.log(event); this.setState({ focused: event.focused }) } render() { return ( <li className="todo__item">
<input type="checkbox"
defaultChecked={this.state.completed}
required={true}
onClick={this.handleComplete.bind(this)}/>
<input type="text"
onChange={this.handleTitleChange.bind(this)}
required={true}
placeholder='title'
value={this.state.title}/>
<button onClick=
{this.handleDelete.bind(this)}>Delete</button>
</li>
) }}export default TodoItem

components/TodoList.js

import React, { Component } from 'react'
import TodoItem from './TodoItem'
class TodoList extends Component { render() { return ( <ul className="todo__list"> { this.props.todos.map((todo) => { if (todo.display === true) { return <TodoItem key={todo.id} todo={todo} actions=
{this.props.actions}/>
} }) } </ul> ) }}export default TodoList

components/App.js

import React, { Component } from 'react'
import TodoInput from './TodoInput'
import TodoList from './TodoList'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from '../redux/actions'
class App extends Component { render() { return (
<div>
<h1 className="header">Todos</h1>
<TodoInput addTodo={this.props.actions.addTodo}/>
<TodoList actions={this.props.actions} todos= {this.props.todos}/>
</div>
) }}function mapStateToProps(state) { return state}function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(actions, dispatch) }}export default connect(mapStateToProps, mapDispatchToProps)(App)

Let’s create some action to Todo List app in your action.js and reducer.js files

redux/reducer.js

function getId(state) {  return state.todos.reduce((maxId, todo) => {    return Math.max(todo.id, maxId)  }, -1) + 1}let reducer = (state, action) => {  switch (action.type) {    case 'ADD_TODO':      return Object.assign({}, state, {        todos: [{          title: action.title,          completed: false,          id: getId(state)        }, ...state.todos]      })    case 'COMPLETE_TODO':      return Object.assign({}, state, {        todos: state.todos.map((todo) => {          return todo.id === action.id ?          Object.assign({}, todo, {updated: false, completed: ! 
todo.completed}) : todo
}) }) case 'DELETE_TODO': return Object.assign({}, state, { todos: state.todos.filter((todo) => { return todo.id !== action.id }) }) default: return state; }}export default reducer

redux/actions.js

let actions = {  addTodo: function(title) {    return {      type: 'ADD_TODO',      title: title,    }  },  completeTodo: function(id) {    return {      type: 'COMPLETE_TODO',      id: id    }  },  deleteTodo: function(id) {    return {      type: 'DELETE_TODO',      id: id     }  }}export default actions

Run the project using the following command

$  npm run serve

And … There you go!

Hope this will help you in creating your own react and redux project. :)

--

--