Why does my code cause an infinite loop and how do I fix this? (ReactJS)[SOLVED]

I am passing the deleteTask(task) function to the child to remove task from state on button click in child

import React from 'react'
import ToDoListItemRow from '../components/ToDoListItemRow.js'
import '../styles/todolist.css'

class ToDoList extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      tasks: ['run', 'eat', 'code', 'sleep'],
      input: ''
    }

    this.handleSubmit = this.handleSubmit.bind(this)
    this.handleChange = this.handleChange.bind(this)
    this.deleteTask = this.deleteTask.bind(this)
  }

  handleSubmit(e) {
    e.preventDefault()
    this.setState({
      tasks: [...this.state.tasks, this.state.input],
      input: ''
    })
  }

  handleChange(e) {
    this.setState({
      input: e.target.value
    })
  }

  deleteTask(task) {
    console.log("this is working")
    this.setState({
      tasks: this.state.tasks.slice(0, task).concat(this.state.tasks.slice(task + 1, this.state.tasks.length))
    })
  }

  render() {
    const tasks = this.state.tasks.map((item, id) => {
        return <ToDoListItemRow key={id} task={item} func={this.deleteTask}/>
    })

    return(
      <div>
        <form onSubmit={this.handleSubmit} className="form">
          <input value={this.state.input} onChange={this.handleChange} type='text' placeholder='What is your task?' className="input-field"/>
          <button type='submit' className="form-submit">Add Task</button>
        </form>
        <ul className="todolist">
          {tasks}
        </ul>
      </div>
    )
  }
}

export default ToDoList

This is the child and i use deleteTask(task) in the onClick function

import React from 'react'
import '../styles/todolist.css'

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

  render() {
    return(
      <div className="todolist-row">
        <li className="task">{this.props.task}</li>
        <button type="button" className="button-delete" onClick={this.props.func(this.props.task)}>Delete</button>
      </div>
    )
  }
}

export default ToDoListItemRow

I am trying to delete a task from an array inside the parent’s state so the child gets deleted when re-rendering but it causes an infinite loop.

Here is the error.

can you provide a link to your project ?

its local :confused: is there any way I can upload to show online?

can you provide a screen shot of the parent and child classes (complete one)

if you want to use any function of parent on child you need to pass it as a prop.

I updated the screenshots to the full classes. I did pass the parent function as prop in the mapping.

You need to render the ToDoListItemRow inside the parent class with the required props and function.

<ToDoListItemRow task={#} func={#} />

I do in the tasks mapping just above the return statement, I then place {tasks} in the ul

 const tasks = this.state.tasks.map((item, id) => {
        return <ToDoListItemRow key={id} task={item} func={this.deleteTask}/>
    })

    return(
      <div>
        <form onSubmit={this.handleSubmit} className="form">
          <input value={this.state.input} onChange={this.handleChange} type='text' placeholder='What is your task?' className="input-field"/>
          <button type='submit' className="form-submit">Add Task</button>
        </form>
        <ul className="todolist">
          {tasks}
        </ul>
      </div>
    )
  }

I fixed the code, passing an argument causes the infinite loop and I have no idea why.

Everything works but the delete function.

Your onclick function needs the function name as the value. You should not call it…

In order to call the function with an argument you need to declare another function that calls the func() with argument;

function <funcName> {
    func(task);
}

<button type="button" className="button-delete" onClick={this.<funcName>}>Delete</button>

nope still loops :(. Good try though! I actually though that was going to work. I am posting the error I get so you can see.

returning e.target.value deletes the task at the beginning of the array, how can i make it so it deletes the this.props.task value from the array?

render() {
    return(
      <div className="todolist-row">
        <li className="task">{this.props.task}</li>
        <button type="button" className="button-delete" onClick={(e) => this.props.deleteTask(e.target.value)}>Delete</button>
      </div>
    )
  }
class ToDoListItemRow extends React.Component {
  constructor(props) {
    super(props)
    
    this.delFunc = this.delFunc.bind(this);
  }

  delFunc() { 
    this.props.func(this.props.i);
  }
  
  render() {
    return(
      <div className="todolist-row">
        <li className="task">{this.props.task}</li>
        <button type="button" className="button-delete" onClick={this.delFunc}>Delete</button>
      </div>
    )
  }
}
deleteTask(task) {
    console.log("this is working")
    console.log(this.state.tasks);
    this.setState((prevState) => ({
       tasks: prevState.tasks.filter((item, i) => {
         return i !== task;
       })
    }));
  }

  render() {
    let tasks = this.state.tasks.map((item, id) => {
        return <ToDoListItemRow key={id} i={id} task={item} func={this.deleteTask}/>
    })

    return(
      <div>
        <form onSubmit={this.handleSubmit} className="form">
          <input value={this.state.input} onChange={this.handleChange} type='text' placeholder='What is your task?' className="input-field"/>
          <button type='submit' className="form-submit">Add Task</button>
        </form>
        <ul className="todolist">
          {tasks}
        </ul>
      </div>
    )
  }
}

check the above code

1 Like

Amazing my friend. How does a task !== i, if i is a number?

and why couldn’t we just send key back instead of i?

both are numbers here…

return <ToDoListItemRow key={id} i={id} task={item} func={this.deleteTask}/>

here i passed ‘i’ as a prop to indicate the index of the corresponding task in array.

this.props.func(this.props.i);

in here i passed that index as argumet for the task to delete.

and finally compared the index in the array matching to the item to be deleted.

tasks: prevState.tasks.filter((item, i) => {
         return i !== task;
       })
1 Like

why couldn’t you just pass the key and filter out the one that matches the key?

coz the key prop is used under the hood by the react comp. It is not exposed for manipulation. if you use ‘this.props.key’ it returns undefined.

1 Like