React: function passed as a prop not working correctly

Hey everyone,

I have written a CRUD fullstack tasks app in react. Here is the code so far:

import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import './styles/styles.scss';
 
class App extends React.Component {
  constructor() {
    super();
    this.handleAddTask = this.handleAddTask.bind(this);
    this.handleDeleteTask = this.handleDeleteTask.bind(this);
    this.handleToggleComplete = this.handleToggleComplete.bind(this);
 
    this.state = {
      tasks: []
    };
  }
 
  getTasks() {
    axios.get('http://localhost:3000/tasks')
      .then(response => {
        this.setState({ tasks: response.data });
      })
      .catch(function (error) {
        console.log(error);
      });
  }
 
  handleAddTask(e) {
    e.preventDefault();
    const newTaskDescription = e.target.elements.task.value.trim();
 
    if(newTaskDescription.length) {
      axios.post('http://localhost:3000/tasks', {
        description: newTaskDescription
      }).then(() => {
        this.getTasks();
      });
 
      e.target.elements.task.value = '';
    }
  }
 
  handleDeleteTask(e) {
    e.preventDefault();
    const taskId = e.target.getAttribute('data-id');
 
    axios.delete(`http://localhost:3000/tasks/${taskId}`)
      .then(() => {
        this.getTasks();
      })
      .catch(function (error) {
        console.log(error);
      });
  }
 
  handleToggleComplete(e) {
    e.preventDefault();
    const taskId = e.target.getAttribute('data-id');
    const tasks = this.state.tasks;
    const taskIndex = tasks.findIndex(task => task._id === taskId);
    tasks[taskIndex].completed = !tasks[taskIndex].completed;
 
    this.setState(() => {
      return { tasks };
    }, () => {
      axios.put(`http://localhost:3000/tasks/${taskId}`, { completed: this.state.tasks[taskIndex].completed})
        .then(() => {
          this.getTasks();
        })
        .catch(function (error) {
          console.log(error);
        });
    });
  }
 
  componentDidMount() {
    this.getTasks();
  }
 
  render() {
    return (
      <div>
        <h1>Here is a list of Tasks</h1>
        
        <form onSubmit={this.handleAddTask}>
          <input type="text" name="task"/>
          <button>Add a task</button>
        </form>
 
        {this.state.tasks.length === 0 && <p>Please add a task to get started</p>}
        <ul>
          {
            this.state.tasks.map((task) => {
              return <li
                key={task._id}
                className={ task.completed ? 'completed' : '' }
              >
                {task.description}
 
                <form 
                  data-id={task._id} 
                  onSubmit={this.handleToggleComplete}
                >
                  <button>Toggle</button>
                </form>
 
                <form 
                  data-id={task._id} 
                  onSubmit={this.handleDeleteTask}
                >
                  <button>Delete</button>
                </form>
              </li>;
            })
          }
        </ul>
 
      </div>
    );
  }
}
 
ReactDOM.render(<App />, document.getElementById('app'));

Everything is working correctly with the above code. However I am currently trying to refactor it. by splitting it up into components like so.

import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import './styles/styles.scss';

class App extends React.Component {
  constructor() {
    super();
    this.handleAddTask = this.handleAddTask.bind(this);
    this.handleDeleteTask = this.handleDeleteTask.bind(this);
    this.handleToggleComplete = this.handleToggleComplete.bind(this);

    this.state = {
      tasks: []
    };
  }

  getTasks() {
    axios.get('http://localhost:3000/tasks')
      .then(response => {
        this.setState({ tasks: response.data });
      })
      .catch(function (error) {
        console.log(error);
      });
  }

  handleAddTask(e) {
    e.preventDefault();
    const newTaskDescription = e.target.elements.task.value.trim();

    if(newTaskDescription.length) {
      axios.post('http://localhost:3000/tasks', {
        description: newTaskDescription
      }).then(() => {
        this.getTasks();
      });

      e.target.elements.task.value = '';
    }
  }

  handleDeleteTask(e) {
    e.preventDefault();
    const taskId = e.target.getAttribute('data-id');

    axios.delete(`http://localhost:3000/tasks/${taskId}`)
      .then(() => {
        this.getTasks();
      })
      .catch(function (error) {
        console.log(error);
      });
  }

  handleToggleComplete(e) {
    e.preventDefault();
    const taskId = e.target.getAttribute('data-id');
    const tasks = this.state.tasks;
    const taskIndex = tasks.findIndex(task => task._id === taskId);
    tasks[taskIndex].completed = !tasks[taskIndex].completed;

    this.setState(() => {
      return { tasks };
    }, () => {
      axios.put(`http://localhost:3000/tasks/${taskId}`, { completed: this.state.tasks[taskIndex].completed})
        .then(() => {
          this.getTasks();
        })
        .catch(function (error) {
          console.log(error);
        });
    });
  }

  componentDidMount() {
    this.getTasks();
  }

  render() {
    return (
      <div>
        <h1>Here is a list of Tasks</h1>
        
        <form onSubmit={this.handleAddTask}>
          <input type="text" name="task"/>
          <button>Add a task</button>
        </form>

        {this.state.tasks.length === 0 && <p>Please add a task to get started</p>}
        <ul>
          {
            this.state.tasks.map((task) => {
              return <Task 
                id={task._id}
                key={task._id}
                className={task.completed ? 'completed' : ''}
                description={task.description}
                handleDeleteTask={this.handleDeleteTask}
                handleToggleComplete={this.handleToggleComplete}
              />;
            })
          }
        </ul>

      </div>
    );
  }
}

const Task = (props) => {
  return (
    <div>
      {props.description}
      <form
        data-id={props._id}
        onSubmit={props.handleToggleComplete}
      >
        <button>Toggle</button>
      </form>

      <form
        data-id={props._id}
        onSubmit={ props.handleDeleteTask}
      >
        <button>Delete</button>
      </form>
    </div>
  );
};

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

And here’s what the data in the API looks like:

0: {completed: false, _id: "5d4eeb8c8925d7368654559d", description: "skaksjakjjsa", __v: 0}
1: {completed: true, _id: "5d4eeb968925d7368654559f", description: "abcdefghijklmnop", __v: 0}
2: {completed: true, _id: "5d4f0de930952a4064afa954", description: "akjkjskjaka", __v: 0}
3: {completed: false, _id: "5d4f10ae2faf70425804a3c4", description: "sasas", __v: 0}
4: {completed: false, _id: "5d4f10b22faf70425804a3c5", description: "ksksksksk", __v: 0}
5: {completed: false, _id: "5d4f10b62faf70425804a3c6", description: "dgdgdgdgdg", __v: 0}
6: {completed: false, _id: "5d4f12792faf70425804a3c7", description: "alallalal", __v: 0}
7: {completed: false, _id: "5d4f12822faf70425804a3c8", description: "abcdefghijklmnop", __v: 0}

However now that I have spit it up and attempted to pass down the handleDeleteTask and handleToggleComplete tasks as props it is telling me that e.target.getAttribute(‘data-id’) is not defined?

Can anyone see the mistake here?

Thanks!

I managed to fix this issue.

In my task component I was accessing the wrong prop. The correct code for the Task component would look like this:

const Task = (props) => {
  return (
    <div>
      {props.description}
      <form
        data-id={props.id}
        onSubmit={props.handleToggleComplete}
      >
        <button>Toggle</button>
      </form>

      <form
        data-id={props.id}
        onSubmit={ props.handleDeleteTask}
      >
        <button>Delete</button>
      </form>
    </div>
  );
};