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>
);
}
}
source to share
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'/>
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.
source to share
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.
source to share
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
source to share
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>
);
}
}
source to share
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.
source to share