I've created a to do list with React. I want to be able to mark tasks pleted simply by clicking on them. I also want to be able to clear task that have been pleted by clicking a button. These two functions are not working correctly with the code I have set up. When I click on an individual todo item to mark it plete, every single to do item on the list gets marked plete and thus, appears with a 'line-through.' When I then click the designated button to clear pleted tasks, absolutely nothing happens. Can someone help me resolve these two issues?
code from App ponent:
class App extends React.Component {
constructor() {
super();
this.state = {
todos: [
{
task: "learn how to fly drone",
id: Date.now(),
pleted: false
},
{
task: "learn React class ponents",
id: Date.now(),
pleted: false
},
{
task: "practice editing videos",
id: Date.now(),
pleted: false
},
{
task: "read Ten Years A Nomad",
id: Date.now(),
pleted: false
}
],
todo: ''
}
}
inputChangeHandler = event => {
this.setState({[event.target.name]: event.target.value})
}
addTask = event => {
event.preventDefault();
let newTask = {
task: this.state.todo,
id: Date.now(),
pleted: false
};
this.setState({
todos: [...this.state.todos, newTask],
todo: ''
})
}
toggleComplete = itemId => {
const todos = this.state.todos.map(todo => {
if (todo.id === itemId) {
todopleted = !todopleted
}
return todo
});
this.setState({todos, todo: ''})
}
clearCompleted = e => {
e.preventDefault();
return this.state.todos.filter(item => !itempleted)
}
render() {
return (
<div className="App">
<h2>Wele to your Todo App!</h2>
<TodoList
todos={this.state.todos}
toggleComplete={this.toggleComplete} />
<TodoForm todos={this.state.todos} value={this.state.todo} inputChangeHandler={this.inputChangeHandler} addTask={this.addTask} clearCompleted={this.clearCompleted}/>
</div>
);
}
}
export default App;
TodoList:
const TodoList = props => {
return (
<div>
{props.todos.map((todo, id) => (
<Todo
todo={todo}
key={id}
toggleComplete={props.toggleComplete} />
))}
</div>
)
}
export default TodoList;
Todo:
const Todo = props => {
return (
<div
key={props.todo.id}
onClick={() => {
props.toggleComplete(props.todo.id)
}}>
<p
style={{textDecoration: props.todopleted ? 'line-through' : 'none'}}>
{props.todo.task}
</p>
</div>
)
}
export default Todo;
TodoForm:
const TodoForm = props => {
return (
<form>
<input
name="todo"
value={props.value}
type="text"
onChange={props.inputChangeHandler}
placeholder="Enter new task" />
<button onClick={props.addTask}>Add Todo</button>
<button onClick={props.clearCompleted}>Clear Completed</button>
</form>
)
}
export default TodoForm;
I've created a to do list with React. I want to be able to mark tasks pleted simply by clicking on them. I also want to be able to clear task that have been pleted by clicking a button. These two functions are not working correctly with the code I have set up. When I click on an individual todo item to mark it plete, every single to do item on the list gets marked plete and thus, appears with a 'line-through.' When I then click the designated button to clear pleted tasks, absolutely nothing happens. Can someone help me resolve these two issues?
code from App ponent:
class App extends React.Component {
constructor() {
super();
this.state = {
todos: [
{
task: "learn how to fly drone",
id: Date.now(),
pleted: false
},
{
task: "learn React class ponents",
id: Date.now(),
pleted: false
},
{
task: "practice editing videos",
id: Date.now(),
pleted: false
},
{
task: "read Ten Years A Nomad",
id: Date.now(),
pleted: false
}
],
todo: ''
}
}
inputChangeHandler = event => {
this.setState({[event.target.name]: event.target.value})
}
addTask = event => {
event.preventDefault();
let newTask = {
task: this.state.todo,
id: Date.now(),
pleted: false
};
this.setState({
todos: [...this.state.todos, newTask],
todo: ''
})
}
toggleComplete = itemId => {
const todos = this.state.todos.map(todo => {
if (todo.id === itemId) {
todo.pleted = !todo.pleted
}
return todo
});
this.setState({todos, todo: ''})
}
clearCompleted = e => {
e.preventDefault();
return this.state.todos.filter(item => !item.pleted)
}
render() {
return (
<div className="App">
<h2>Wele to your Todo App!</h2>
<TodoList
todos={this.state.todos}
toggleComplete={this.toggleComplete} />
<TodoForm todos={this.state.todos} value={this.state.todo} inputChangeHandler={this.inputChangeHandler} addTask={this.addTask} clearCompleted={this.clearCompleted}/>
</div>
);
}
}
export default App;
TodoList:
const TodoList = props => {
return (
<div>
{props.todos.map((todo, id) => (
<Todo
todo={todo}
key={id}
toggleComplete={props.toggleComplete} />
))}
</div>
)
}
export default TodoList;
Todo:
const Todo = props => {
return (
<div
key={props.todo.id}
onClick={() => {
props.toggleComplete(props.todo.id)
}}>
<p
style={{textDecoration: props.todo.pleted ? 'line-through' : 'none'}}>
{props.todo.task}
</p>
</div>
)
}
export default Todo;
TodoForm:
const TodoForm = props => {
return (
<form>
<input
name="todo"
value={props.value}
type="text"
onChange={props.inputChangeHandler}
placeholder="Enter new task" />
<button onClick={props.addTask}>Add Todo</button>
<button onClick={props.clearCompleted}>Clear Completed</button>
</form>
)
}
export default TodoForm;
Share
Improve this question
asked Dec 27, 2019 at 2:10
Jevon CochranJevon Cochran
1,7842 gold badges16 silver badges30 bronze badges
2 Answers
Reset to default 51) The reason why every item is marked is because all objects within the todos
state has the same id. Therefore, toggleComplete()
will end up marking all objects within todos
as true
.
What you can do is to assign each object with a unique id
, instead of assigning all id
with the same Date
object.
Here is an example:
constructor() {
super();
this.state = {
todos: [
{
task: "learn how to fly drone",
id: 1,
pleted: false
},
{
task: "learn React class ponents",
id: 2,
pleted: false
},
{
task: "practice editing videos",
id: 3,
pleted: false
},
{
task: "read Ten Years A Nomad",
id: 4,
pleted: false
}
],
todo: ''
}
}
2) Next, clearCompleted
is not calling setState()
, hence, none of the tasks are cleared. I assume that you are trying to set pleted
as false? In that case, you can simply do set pleted as false
for all objects, and then update your state.
clearCompleted = e => {
e.preventDefault();
const todos = this.state.todos.map(todo => ({
...todo,
pleted: false,
}));
this.setState({
todos,
});
}
I have created a demo which fixes your issues.
Edit:
@wentjun's answer should be selected as the accepted answer.. I am going to leave this answer up though, as I still feel it provides value. To elaborate: I prefer to pass the index as it's a little faster then having to map
over every single todo item just to find the one that was clicked. (Even if you used this.state.todos.find(itm => itm.id === itemId
, passing the index is faster since you already know which item was clicked..
Original Answer:
I modified the ToDo
ponent to pass the entire todo object in the click event, as well as passing the todo item index as well as the todo item within the ToDoList
ponent. This way you can grab the index of the todo item that was clicked, to easily change the pleted
property on that specific todo item within state.
Even though I am not doing anything with the todo item object that is being passed via the click event, I remend still passing the entire object, just in case - makes things more flexible. Open up your console to see the todo object that gets clicked.
Edit: I updated the clearCompleted
to:
clearCompleted = e => {
e.preventDefault();
let stateCopy = {...this.state};
stateCopy.todos = stateCopy.todos.reduce((acc, cur) =>
[...acc, {...cur, pleted: false}], []
);
this.setState(stateCopy);
};
I also modified your setState
call within the click handler.. It is best practice to always make a copy of your state, modify the copy, then update state with the copy.
Demo:
class App extends React.Component {
constructor() {
super();
this.state = {
todos: [
{
task: "learn how to fly drone",
id: Date.now(),
pleted: false
},
{
task: "learn React class ponents",
id: Date.now(),
pleted: false
},
{
task: "practice editing videos",
id: Date.now(),
pleted: false
},
{
task: "read Ten Years A Nomad",
id: Date.now(),
pleted: false
}
],
todo: ""
};
}
inputChangeHandler = event => {
this.setState({ [event.target.name]: event.target.value });
};
addTask = event => {
event.preventDefault();
let newTask = {
task: this.state.todo,
id: Date.now(),
pleted: false
};
this.setState({
todos: [...this.state.todos, newTask],
todo: ""
});
};
toggleComplete = (todoItem, todoItemIndex) => {
let stateCopy = { ...this.state };
let item = stateCopy.todos[todoItemIndex];
item.pleted = !item.pleted;
this.setState(stateCopy, () =>
console.log(this.state.todos[todoItemIndex])
);
};
clearCompleted = e => {
e.preventDefault();
let stateCopy = {...this.state};
stateCopy.todos = stateCopy.todos.reduce((acc, cur) =>
[...acc, {...cur, pleted: false}], []
);
this.setState(stateCopy);
};
render() {
return (
<div className="App">
<h2>Wele to your Todo App!</h2>
<TodoList
todos={this.state.todos}
toggleComplete={this.toggleComplete}
/>
<TodoForm
todos={this.state.todos}
value={this.state.todo}
inputChangeHandler={this.inputChangeHandler}
addTask={this.addTask}
clearCompleted={this.clearCompleted}
/>
</div>
);
}
}
const TodoList = ({ todos, toggleComplete }) => {
return (
<div>
{todos && todos.map((todo, index) => (
<Todo
todo={todo}
key={index}
toggleComplete={() => toggleComplete(todo, index)} /* <<--- Pass the item and index to the handler function */
/> /* Even though we are not using the actual todo item */
/* object, still not a bad idea to pass it thru */
))}
</div>
);
};
const Todo = props => {
return (
<div
key={props.todo.id}
onClick={() => { props.toggleComplete(props.todo) }}> {/* Pass entire todo object, just in case you need it */}
<p
style={{
cursor: 'pointer',
textDecoration: props.todo.pleted ? "line-through" : "none"
}}>
{props.todo.task}
</p>
</div>
);
};
const TodoForm = props => {
return (
<form>
<input
name="todo"
value={props.value}
type="text"
onChange={props.inputChangeHandler}
placeholder="Enter new task"
/>
<button onClick={props.addTask}>Add Todo</button>
<button onClick={props.clearCompleted}>Clear Completed</button>
</form>
);
};
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare./ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744795880a4594199.html
评论列表(0条)