9
votes

I'm making a simple todo app, where i have put in the logic to edit and delete the todos as well. I'm trying to update the parent state from child component but when i'm trying to click on delete it is throwing me an error e.preventDefault() is not a function and it is removing all of the todos here are the components:

PARENT

export default class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
       listArr: [],
    }

  }


 deleteTodos(i) {
   var lists = this.state.listArr;
   lists.splice(i, 1);
   this.setState({listArr: lists})
 }

render() {
 .......
 <ToDoList {...this.state} passDeleteTodos={this.deleteTodos} />
......

}

CHILD

export class ToDoList extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
              editing: false,
            };

        handleDelete(e, i) {
            e.preventDefault();
            this.props.passDeleteTodos()
        }


    renderDisplay() {
        return(
          <div>
           {
            this.props.listArr.map((list,i) => {
               return(
                 <div key={i} index={i} ref="text">
                  <li>{list}
                    <div style={{float: 'right'}}>
                     <button className="btn btn-danger btn-xs glyphicon glyphicon-trash"
                             onClick={() => this.handleDelete(i)}
                     />
                    </div>
                </div>
          </div>
6

6 Answers

13
votes

You need to pass the event object to handleDelete function when you make use of Arrow function as done in your implementation.

You can think of an arrow function like a function that calls another function to which you need to pass the arguments. Event object is a parameter to the arrow function and you indeed need to pass this on to the handleDelete function

onClick={(e) => this.handleDelete(e, i)}

However after this change you still need to bind the deleteTodos function in the parent, since the context of this inside this function won't be that of the React class component, you can do it like

deleteTodos = (i) =>  {
   var lists = this.state.listArr;
   lists.splice(i, 1);
   this.setState({listArr: lists})
 }

or

constructor(props){
    super(props);
    this.state = {
       listArr: [],
    }
    this.deleteTodos = this.deleteTodos.bind(this);
  }
1
votes

I change e.preventDefault() => e.preventDefault and bind the function.

Example

export default class App extends React.Component {
 constructor(props) {
   super(props)
   this.state = {
    listArr: [],
   }

  this.deleteTodos = this.deleteTodos.bind(this)

  }
  handleDelete(e, i) {
        e.preventDefault;
        this.props.passDeleteTodos()
        ...
    }

   render() {
    return(
      <div>
       {
        this.props.listArr.map((list,i) => {
           return(
             <div key={i} index={i} ref="text">
              <li>{list}
                <div style={{float: 'right'}}>
                 <button className="btn btn-danger btn-xs glyphicon glyphicon-trash"
                         onClick={(e,i) => this.handleDelete(e,i)}
                 />
                </div>
            </div>
       )}
      }
      </div>
0
votes

You are not sending e to the correspondent method.

You could also bind the event onClick={this.handleDelete.bind(this, i)}

Same applies for deleteTodos in the App component.

Either way you can use the same approach or bind it in the constructor:

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
       listArr: [],
    }

    this.deleteTodos = this.deleteTodos.bind(this)
  }

  ...
}
0
votes

doesn't behave the same way as an so you can't expect the same preventDefault call.

But your problem is you in bind the order of params change. So you're binded param becomes first in the function. See my snippet below.

const App = () => {
  const _click = (externalVar, e) => {
    console.log("PARAMS", externalVar, e);
  };
  
  const externalVar = 1
  
  return (
    <button onClick={_click.bind(undefined, externalVar)}>click me</button>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>

Like it says here

fun.bind(thisArg[, arg1[, arg2[, ...]]])

arg1, arg2, ... Arguments to prepend to arguments provided to the bound function when invoking the target function.

0
votes

arrow function in react doesn't need to bind to this. But during call to the functions, for example to call this function handleDelete

 handleDelete(e, i) {
        e.preventDefault();
        this.props.passDeleteTodos()
    }

we will use synatx as:

handleDelete.bind(i)
-1
votes
handleDelete(e, i) {
    e.preventDefault();
    this.props.passDeleteTodos()
    ...
}

onClick={(e,i) => this.handleDelete(e,i)}

if the above code is not working properly try this.

handleDelete(i) {
    this.props.passDeleteTodos()
    ...
}

onClick={(e) => {e.preventDefault(); this.handleDelete(i)}}