ReactJS | Create ToDo Application in ReactJS using Class Components

ReactJS is a powerful library to build robust applications with the help of manageable and reusable components. React renders application without using any HTML templates which improves application performance many folds. In class components of ReactJS, we use JSX which is JavaScript with XML.

React maintains a Virtual DOM which is React Element, converted from JSX and a Real DOM element which is visible to the user on the screen.

In this post, we will create a very basic but component rich ToDo application using many class components and will also understand some basic concepts used in our app.

Let’s begin

First, make sure you have the latest version of NodeJS installed. After that, we will install Creat React App toolchain

$ npm i -g create-react-app
$ create-react-app react-todo-app
$ cd react-todo-app

After that run application by running the following command

$ npm start

Now you will see React JS app running

In our ToDo application, we will use Bootstrap for a nice user interface. Run following in CMD

$ npm i bootstrap

To include Bootstrap in the application, we need open index.js file then import the bootstrap.css as shown below

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.css';

ReactDOM.render( <App /> , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

If you look closely at line 6 we have

ReactDOM.render( <App /> , document.getElementById('root'));

that means on initialization App component will be loaded in the element having ID root

In file App.js file there is a Class Component with the following code

import React, { Component } from 'react';
import './App.css';
import Todos from './components/todos';

class App extends Component {

  render() {
    return (
      <div className="container">
        <h1 className="text-center">ToDo App in ReactJS</h1>
        <Todos />
      </div>
    );
  }
}

export default App;

Here we have another Component Todos, which is another Class Component to show List of Todos using Todo and one more Class Component AddTodo to show Add Todo input with a button.

Let’s create these three new Class Components (Todos, Todo & AddTodo) under components folder at “~components“. After adding these components our app directory will look like this

 

todos.jsx

In Todos class there is state property for adding dynamic content in Component. state is a special property in React component it is basically an object to keep data which we will use in our class component. a state is a private object only accessible to the component itself.

In above Class Component Todos, we have added some methods which are defined as arrow notation as we are going to access these methods from other Components like Todo and AddTodo.

getTime() is getting used to adding an ID to ToDo, this is without arrow notation as we are using this method locally.

handleDone, handleDelete and addNewTodo are method whose references are getting passed as Component attributes, which will be called from their respective Components.

import React, { Component } from 'react';

import Todo from './todo';
import AddTodo from './addtodo';

class Todos extends Component {

    //Component state with default values
    state = {
        addTodoValue: "",
        todos: [
            {
                id: 1,
                value: "todo 1",
                isDone: false
            },
            {
                id: 2,
                value: "todo 2",
                isDone: true
            },
            {
                id: 3,
                value: "todo 3",
                isDone: false
            }
        ]
    }

    //Local helper method to get date
    getTime() {
        let d = new Date();
        var n = d.getTime();
        return n;
    }

    //method called from Todo component
    handleDelete = todo => {
        const todos = this.state.todos.filter((t) => {
            return t.id !== todo
        });
        this.setState({ todos });
    }

    handleDone = todo => {
        const todos = [...this.state.todos];
        todos.map((t) => {
            if (t.id === todo.id) {
                t.isDone = !t.isDone;
            }
            return t;
        });
        this.setState({todos});
    }

    //method called from AddTodo component
    addNewTodo = value => {
        if (value) {
            const todos = [...this.state.todos];
            todos.push(
                {
                    id: this.getTime(),
                    value: value,
                    isDone: false
                }
            );
            this.setState({ addTodoValue: "", todos })
        } else {
            console.log("Please Add Todo Text");
        }
    }

    render() {
        return (
            <table className="table">
                <tbody>
                    {this.state.todos.map((todo, index) => (
                        <tr key={todo.id}>
                            <Todo index={index+1} todo={todo} fooDelete={this.handleDelete} fooDoneDone={this.handleDone} />
                        </tr>
                    ))}
                    <tr>
                        <td colSpan="4" className="text-center">
                            <AddTodo fooAddTodo={this.addNewTodo} addTodoValue={this.state.addTodoValue} />
                        </td>
                    </tr>
                </tbody>
            </table>
        );
    }
}

export default Todos;

 

todo.jsx

Todo Class will represent a single Todo in the list and having methods fooDoneDone ( check/ uncheck event handler ) and fooDelete ( delete button event handler )

import React, { Component } from 'react';

class Todo extends Component {

    render() {
        return (
            <React.Fragment >
                <td style={{ width: 10 }} className="text-center">
                    {this.props.index}
                </td>
                <td style={{ width: 15 }} className="text-center">
                    <input type="checkbox" defaultChecked={this.props.todo.isDone}  onChange={() => this.props.fooDoneDone(this.props.todo)} />
                </td>
                <td>
                    {
                        this.renderTodo()
                    }
                </td>
                <td style={{ width: 100 }} className="text-center">
                    <button onClick={() => this.props.fooDelete(this.props.todo.id)} className="btn btn-danger btn-sm">Delete</button>
                </td>
            </React.Fragment>
        );
    }

    renderTodo(){
        if(this.props.todo.isDone)
        return <s>{this.props.todo.value}</s>;
        else
        return this.props.todo.value;
    }

}

export default Todo;

 

 

addtodo.jsx

In AddTodo class we have handleChange method also in arrow notation as we need to call the setState method to replicate changes in view. setState will be undefined if we don’t have arrow method.

import React, { Component } from 'react';

class AddTodo extends Component {
    state = {
        defaultValue: "",
        value: this.props.addTodoValue
    }

    handleChange = (e) => {
        //Updating local component state
        this.setState({
            value: e.target.value
        });
    }

    clearInput = () => {
        //Clear existing value in input
        document.getElementById("todoValue").value = "";
        
        //Updating local component state
        this.setState({value:""});
    }

    addTodo = () => {
        //Call method reference in Todos component using props
        this.props.fooAddTodo(this.state.value);
        this.clearInput();
    }

    render() {
        return (
            <div className="input-group mb-3">
                <input type="text" className="form-control" id="todoValue" placeholder="ToDo" onChange={this.handleChange} />
                <div className="input-group-append">
                    <button onClick={this.addTodo} className="btn btn-outline-secondary" type="button" id="button-addon2">Add New ToDo</button>
                </div>
            </div>
        );
    }
}

export default AddTodo;

So here we discussed a simple ToDo application in React JS and also used props to access values and methods from other components and state object to use it to define local values. We will discuss more React JS examples in coming articles.

Leave a Comment

Your email address will not be published. Required fields are marked *