reactjsclassthisreact-class-based-component

reactjs- A custom function that passed as a prop into an component that inside of map function doesn't work


I am doing a to-do app but with Class Components in React because I wanted to learn logic of state before Hooks.

I have only one index.js file so it's not that complicated.

These are my packages:

var React = require("react");
var ReactDOM = require("react-dom/client");

This is my first root component:

class TodoComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      todos: ["wash up", "eat some cheese", "take a nap", "buy flowers"],
    };
  }

  onDelete(item) {
    var updatedTodos = this.state.todos.filter(function (val, index) {
      return item !== val;
    });

    this.setState({
      todos: updatedTodos,
    });
  }

  render() {
    var todos = this.state.todos;
    todos = todos.map(
      function (item, index) {
        return <TodoItem item={item} key={index} onDelete={this.onDelete} />;
      }.bind(this)
    );

    return (
      <div id="todo-list">
        <p>The busiest people have the most leisure...</p>
        <ul>{todos}</ul>
      </div>
    );
  }
}

And This is my second component:

class TodoItem extends React.Component {
  constructor(props) {
    super(props);
  }

  handleDelete() {
    this.props.onDelete(this.props.item);
  }

  render() {
    return (
      <li>
        <div className="todo-item">
          <span className="item-name">{this.props.item}</span>
          <span className="item-delete" onClick={this.handleDelete}>
            {" "}
            x
          </span>
        </div>
      </li>
    );
  }
}

And createRoot:

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<TodoComponent />);

There wasn't any problem till I wanted to delete the items in the list. When I click the "x" that inside of span it shows me this error:

Cannot read properties of undefined (reading 'onDelete')
TypeError: Cannot read properties of undefined (reading 'onDelete')

I was expecting to delete the item obviously.

I have checked this question:

reactjs - Function not working inside a map function

but I had already did this in my code.

So I wrote

console.log(this.props.onDelete);

into the render method of TodoItem component to check wheter I did succeed or not to pass the onDelete method as a property and actually yes I am getting the code perfectly.

At this point I couldn't find any other solution. I am just guessing that this happens because my lack of knowlodge of this keyword's scope or something about using component inside of map function.

Because in here my problem looks kinda close to that link

reactjs - Function not working inside a map function

and they solve it by just putting

.bind(this)

but it didn't work out on mine code so only difference between mine and his code is I am using a component inside the map function and he just use an li tag.


Solution

  • You need to bind from both TodoComponent and TodoItem. In TodoComponent bind the onDelete method

    constructor(props) {
        super(props);
    
        this.state = {
          todos: ["wash up", "eat some cheese", "take a nap", "buy flowers"]
        };
    
        this.onDelete = this.onDelete.bind(this);
      }
    

    In TodoItem component bind the handleDelete method

    constructor(props) {
        super(props);
    
        // Bind the method in the constructor
        this.handleDelete = this.handleDelete.bind(this);
      }
    

    Check the codesandbox for full functional code

    https://codesandbox.io/s/react-class-component-forked-4gk8l2?file=/src/TodoItem.js:71-210