Search code examples
javascriptreactjsmongodbmern

Deleting a todo - MERN Stack


I have been trying to delete a todo when the delete button is clicked but instead the browser throws a runtime error saying: "TypeError: props.deleteTodo is not a function"

I have double checked the method name too and they match and I don't know why the onclick function is not recognised. Any help would be appreciated.

Here's my code:

import React, { Component } from "react";
import { Link } from "react-router-dom";
import axios from "axios";

const Todo = props => (
  <tr>
    <td className={props.todo.todo_status === "Completed" ? 'completed' : props.todo.todo_status === "In Progress" ? 'inprogress' : ''}>{props.todo.todo_desc}</td>
    <td className={props.todo.todo_status === "Completed" ? 'completed' : props.todo.todo_status === "In Progress" ? 'inprogress' : ''}>{props.todo.todo_responsible}</td>
    <td className={props.todo.todo_status === "Completed" ? 'completed' : props.todo.todo_status === "In Progress" ? 'inprogress' : ''}>{props.todo.todo_priority}</td>
    <td className={props.todo.todo_status === "Completed" ? 'completed' : props.todo.todo_status === "In Progress" ? 'inprogress' : ''}>{props.todo.todo_status}</td>
    <td className={props.todo.todo_status === "Completed" ? 'completed' : props.todo.todo_status === "In Progress" ? 'inprogress' : ''}>
      <Link to={'/edit/' + props.todo._id}>Edit</Link>&emsp;
      <button type="button" className="btn-danger" onClick={() => { props.deleteTodo(props.todo._id); }}>Delete</button>
    </td>
  </tr>
)

export default class TodoList extends Component {

  constructor(props) {
    super(props);
    this.deleteTodo = this.deleteTodo.bind(this);
    this.state = { todos: [] };
  }

  componentDidMount() {
    axios.get('http://localhost:8082/todos/')
      .then(res => {
        this.setState({ todos: res.data })
      })
      .catch(function (error) {
        console.log(error);
      });
  };

  deleteTodo = (id) => {
    axios.delete("http://localhost:8082/todos/delete/" + id)
      .then(res => {
        console.log(res.data)
        this.props.history.push("/");
      })
      .catch(err => {
        console.log("Error Deleting Todo");
      })
  };

  componentDidUpdate() {
    axios.get('http://localhost:8082/todos/')
      .then(res => {
        this.setState({ todos: res.data })
      })
      .catch(function (error) {
        console.log(error);
      });
  }

  todoList() {
    return this.state.todos.map(function (currentTodo, i) {
      return (
        <Todo
          todo={currentTodo}
          key={i} />
      );
    });
  };

  render() {
    return (
      <div>
        <h3>Todos List</h3>
        <table className="table table-hover" style={{ marginTop: 20 }}>
          <thead>
            <tr>
              <th>Description</th>
              <th>Responsible</th>
              <th>Priority</th>
              <th>Status</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            {this.todoList()}
          </tbody>
        </table>
      </div>
    )
  }
}

Error Image


Solution

  • You defined and bound deleteTodo, but you didn't pass it down as a prop from the parent when calling <Todo. This:

    todoList(){
        return this.state.todos.map(function (currentTodo,i){
            return (
                <Todo 
                    todo={ currentTodo } 
                    key={ i } />
            );
        });
    };
    

    should be

    todoList(){
        return this.state.todos.map((currentTodo, i) => (
            <Todo
                todo={currentTodo}
                key={i}
                deleteTodo={this.deleteTodo} />
        ));
    };
    

    You can also remove the line

    this.deleteTodo = this.deleteTodo.bind(this);
    

    entirely, since you're using a class field with an arrow function.