ReactJS – Todo List with Class Components

Simple Todo example list using class components.

The Parent Class will be called TodoParent

import React, { Component } from 'react'
import Todos from './Todos';

export class TodoParent extends Component {
  
    state = {        
        todo: [
          { id: 1, title: 'Go out', completed: false },
          { id: 2, title: 'Go home', completed: false },
          { id: 3, title: 'Go to bed', completed: false }
        ]     
      }
  
    markComplete = (id) => {        
        this.setState({ todo: this.state.todo.map(todo => {
            if(todo.id === id) {
               todo.completed = !todo.completed;
            }                    
            return todo;
     })});
    }

    render() {
        return (
            <div>
                <Todos todo={this.state.todo} markComplete={this.markComplete}/>
            </div>
        )
    }
}

export default TodoParent

The first child class will be the Todo class component.

import React, { Component } from 'react'
import TodoItem from './TodoItem';

export class Todos extends Component {

    render() {
        return this.props.todo.map((prop) => (
        <TodoItem key={prop.id} todo={prop} markComplete={this.props.markComplete}/>
        ));            
    }
}

export default Todos

Finally, the grandchild component called from the Todo component that renders the check box and list.

import React, { Component } from 'react'

export class TodoItem extends Component {
    getStyle = () => {
        return {
            textDecoration: this.props.todo.completed ? 
            'line-through' : 'none'
        }
    }

    render() {    
        const { id, title } = this.props.todo;
        return (
            <div style={this.getStyle()}>
                <p>
                    <input type="checkbox" onChange={this.props.markComplete.bind(this, id)}  /> {' '}
                    { this.props.todo.title }
                </p>
            </div>
        )
    }
}

export default TodoItem

Passing data down

The State is created and handled from the TodoParent and is passed down to the Todo class component with a PROP.

The Todo Component then passes a Key and the Todo information with the PROP.

Sending change back up from the Grandchild component to the Parent.

Create a function called mark complete in the TodoParent Component.

  markComplete = (id) => {        
        this.setState({ todo: this.state.todo.map(todo => {
            if(todo.id === id) {
               todo.completed = !todo.completed;
            }                    
            return todo;
     })});
    }

Pass the arrow funciton (this.markComplete) down to the child component Todo along with the PROP into a variable called markComplete.
markComplete={this.markComplete}

<Todos todo={this.state.todo} markComplete={this.markComplete}/>

From the Todo Component then pass it one more time to the Grandchild component but since it is a PROP it must be sent to another variable labeled
markComplete as this.props.markComplete.

<TodoItem key={prop.id} todo={prop} markComplete={this.props.markComplete}/>

In the Grandchild component bind the ID value to the this.props.markComplete with the bind property. This is the value that will get transmitted up to the Parent/

<input type="checkbox" onChange={this.props.markComplete.bind(this, id)}  /> 

Remember in the Parent markComplete function we want to change the state of complete to the opposite of what it is.

todo.completed = !todo.completed;

So if true then false or if false then true. but we need the id to change the correct record.

The function markComplete from the Parent component will get the id from the grandchild that bind the (id) with
{this.props.markComplete.bind(this, id)}.
Then the Todo list is updated with todo: this.state.todo.map(todo => {}. The the todo.complete changes to true or false for the record identified by todo.id that equals the id that was bound from the Grandchild.

  markComplete = (id) => {        
        this.setState({ todo: this.state.todo.map(todo => {
            if(todo.id === id) {
               todo.completed = !todo.completed;
            }                    
            return todo;
     })});