How to remove ToDo element onClick in React?

I am making a simple todo app with React, just for practice. How can I remove a list item by clicking on it?

Here is my todos.js

export default class Todos extends Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], text: '' };
    }

    addTodo(e) {
        e.preventDefault();
        this.setState({ todos: [ this.state.text, ...this.state.todos ] });
        this.setState({ text: ''});
    }

    updateValue(e) {
        this.setState({ text: [e.target.value]})
    }

    render() {
        return(
            <div>
                <form onSubmit = {(e) => this.addTodo(e)}>
                    <input
                        placeholder="Add Todo"
                        value={this.state.text}
                        onChange={(e) => {this.updateValue(e)}}
                    />
                    <button type="submit">Add Todo</button>
                </form>
                <TodoList todos={this.state.todos}/>
            </div>
        );
    }
}

      

And here is TodoList.js where I am trying to remove a list item.

import React, { Component } from 'react';
import { connect } from 'react-redux';

export default class TodoList extends Component {
    removeItem(e) {
        // splice this.props.todos??
    }
    render() {
        return(
            <ul>
                { this.props.todos.map((todo) => {
                    return <li onClick={(e) => { this.removeItem(e)}} key={todo}>{ todo }</li>
                })}
            </ul>
        );
    }
}

      

+6


source to share


7 replies


To remove todo objects, first pass the function from the parent component:

<TodoList todos={this.state.todos} removeTodo={this.removeTodo}/>

      

Bind this function in constructor

:

this.removeTodo = this.removeTodo.bind(this);

      

Define this function in the parent component, it will remove this element from the variable state

:

removeTodo(name){
    this.setState({
        todo: this.state.todo.filter(el => el !== name)
    })
}

      

Then the inner child component calls this method to remove the todo:

export default class TodoList extends Component {
    removeItem(e) {
        this.props.removeTodo(item);
    }
    render() {
        return(
            <ul>
                { this.props.todos.map((todo) => {
                    return <li onClick={() => { this.removeItem(todo)}} key={todo}>{ todo }</li>
                })}
            </ul>
        );
    }
}

      

Sentence:



Don't call setState

multiple times in function

if you want to set multiple values state

, then write like this:

this.setState({
    a: value1,
    b: value2,
    c: value3
})

      

Working example:

class Todos extends React.Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], text: '' };
        this.removeTodo = this.removeTodo.bind(this);
    }

    addTodo(e) {
        e.preventDefault();
        this.setState({ 
        	todos: [ this.state.text, ...this.state.todos ],
        	text: ''
        });
    }

    removeTodo(name, i){
        let todos = this.state.todos.slice();
        todos.splice(i, 1);
        this.setState({
            todos
        });
    }

    updateValue(e) {
        this.setState({ text: e.target.value})
    }

    render() {
        return(
            <div>
                <form onSubmit = {(e) => this.addTodo(e)}>
                    <input
                        placeholder="Add Todo"
                        value={this.state.text}
                        onChange={(e) => {this.updateValue(e)}}
                    />
                    <button type="submit">Add Todo</button>
                </form>
                <TodoList todos={this.state.todos} removeTodo={this.removeTodo}/>
            </div>
        );
    }
}

class TodoList extends React.Component {

    removeItem(item, i) {
        this.props.removeTodo(item, i);
    }

    render() {
        return(
            <ul>
                { this.props.todos.map((todo,i) => {
                    return <li onClick={() => { this.removeItem(todo, i)}} key={i}>{ todo }</li>
                })}
            </ul>
        );
    }
}

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

<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='app'/>
      

Run codeHide result


Update:

This is for doubt @ whs.bsmith, the code I suggested will work as expected in the case where the user adds unique items to the todo list if he tries to add the same item he won't reflect in the ui because the OP is using the name the todo list because the key and key must be unique.

To solve this problem:

In the working snippet, I have used indices instead of the name of todo objects for the key, which will work correctly and it will allow the user to add the same item multiple times and when removed, it will only remove that specific item and not the entire item having the same name but it is not recommended to use indexes as a key.

+11


source


You called setState two times in the addTodo function . You can set todos and text in one setState function like this:

addTodo(e) {
    e.preventDefault();
    this.setState({ todos: [ this.state.text, ...this.state.todos ], text: '' });
}

      

Don't put removeItem in the TodoList component as it works exclusively on props. Pass removeItem from Todos to it and remove this item in the Todos removeItem function like this:



import React, {Component} from 'react'
export default class Todos extends Component {
  constructor(props) {
    super(props);
    this.state = { todos: [], text: '' };
    this.removeItem = this.removeItem.bind(this)
  }

  addTodo(e) {
    e.preventDefault();
    this.setState({ todos: [ this.state.text, ...this.state.todos ], text: '' });
  }


  updateValue(e) {
    this.setState({ text: [e.target.value]})
  }
  removeItem(index) {
    const todos = this.state.todos.filter((todo, todoIndex) => {
      return todoIndex !== index
    })
    this.setState({ todos })
  }
  render() {
    return(
      <div>
        <form onSubmit = {(e) => this.addTodo(e)}>
          <input
            placeholder="Add Todo"
            value={this.state.text}
            onChange={(e) => {this.updateValue(e)}}
            />
          <button type="submit">Add Todo</button>
        </form>
        <TodoList todos={this.state.todos} removeItem={this.removeItem} />
      </div>
    );
  }
}

class TodoList extends Component {
  render() {
    return(
      <ul>
        { this.props.todos.map((todo, index) => {
          return <li onClick={(e) => { this.props.removeItem(index)}} key={todo}>{ todo }</li>
        })}
      </ul>
    );
  }
}

      

Hope this helps.

+2


source


The only thing to do is move todos

to state

in TodoList

and pass the index of the current todo to the method removeItem

. Then splice it as you suggested.

In TodoList

:

constructor(props) {
    super(props);

    this.state = { todos: props.todos };
}

removeItem(index) {
    this.setState({
        todos: this.state.todos.filter((_, i) => i !== index)
    });
}

render() {
    return(
        <ul>
            { this.state.todos.map((todo, index) => {
                return <li onClick={(e) => { this.removeItem(index)}} key={todo}>{ todo }</li>
            })}
        </ul>
    );
}

      

Remove item from Remove item from array in component state

0


source


First, you want the removeItem method to exist in the Todos class since this state is preserved. Then you can use filter or slice

export default class Todos extends Component {
    constructor(props) {
        super(props);
        this.state = { todos: [], text: '' };
    }

    addTodo(e) {
        e.preventDefault();
        this.setState({ todos: [ this.state.text, ...this.state.todos ] });
        this.setState({ text: ''});
    }

    updateValue(e) {
        this.setState({ text: [e.target.value]})
    }

    removeItem = index => {
        //either use filter
        const { todos } = this.state;
        const newTodos = todos.filter((todo, i) => i !== index);
        this.setState({ todos: newTodos});

        //or use slice
        const slicedNewTodos = todos.slice(0, index).concat(todos.slice(index + 1));
        this.setState({ todos: slicedNewTodos});
    }

    render() {
        return(
            <div>
                <form onSubmit = {(e) => this.addTodo(e)}>
                    <input
                        placeholder="Add Todo"
                        value={this.state.text}
                        onChange={(e) => {this.updateValue(e)}}
                    />
                    <button type="submit">Add Todo</button>
                </form>
                <TodoList removeItem={this.removeItem} todos={this.state.todos}/>
            </div>
        );
    }
}

import React, { Component } from 'react';
import { connect } from 'react-redux';

class TodoList extends Component {
    render() {
        return(
            <ul>
                { this.props.todos.map((todo, index) => {
                    return <li onClick={() => this.props.removeItem(index)} key={todo}>{ todo }</li>
                })}
            </ul>
        );
    }
}

      

0


source


class TodoList extend React.Component{
    constructor(props){
        super(props);
        this.state = {
            todos: [],
            todo: ''
        }
        this.changeTodo = this.changeTodo.bind(this);
        this.addTodo = this.addTodo.bind(this);
        this.removeTodo = this.removeTodo.bind(this);
    }

    changeTodo(event){
        this.setState({
            todo: event.target.value
        })
    }

    addTodo(){
        let { todo, todos } = this.state;
        this.setState({
            todo: '',
            todos: [...todos, todo]
        })
    }

    removeTodo(index){
        let { todos } = this.state;
        todos.splice(index, 1);
        this.setState({
            todos: todos
        })
    }

    render(){
        let { todo, todos } = this.state;
        return <div>
            <input value={todo} onChange={this.changeTodo}/>
            <button onClick={this.addTodo}>Add Todo</button>
            {
                todos.map((todo, index)=>{
                    return <h1 onClick={this.removeTodo.bind(undefined, index)} key={index}>{todo}</h1>
                })
            }
        </div>
    }
}

      

This is a small example for TodoList. Walk through this code to see how to delete a todo in the TodoList app.

0


source


Each gave a good overview. I find this is a good way to remove a task from the task list.

todolist.splice (index, 1);

Splice removes 1 element from the startIndex index (in the todolist array)

0


source


But isn't splice () a method that changes state? I learned that the state should be immutable.

0


source







All Articles