I'm trying to make a simple todo in react. I want to be able to click in the button next to the todo text and mark it as complete, with a line passing through it, so I guess the point of the button would be to toggle between the two stylings. But I don't know how to apply the styling to that specific todo. Here's my code so far:
import React, { useState } from 'react';
function App() {
const [todos, setTodos] = useState([])
const toggleComplete = (i) => {
setTodos(todos.map((todo, k) => k === i ? {
...todo, complete: !todo.complete
} : todo))
}
const handleSubmit = (event) => {
event.preventDefault()
const todo = event.target[0].value
setTodos((prevTodos) => {
return [...prevTodos, {
userTodo: todo, completed: false, id: Math.random().toString()
}]
})
}
return (
<div>
<form onSubmit={handleSubmit}>
<input placeholder='name'></input>
<button type='submit'>submit</button>
</form>
<ul>
{todos.map((todos) => <li key={todos.id}>
<h4>{
todos.completed ? <s><h4>{todos.userTodo}</h4></s> : <h4>{todos.userTodo}</h4>}
</h4>
<button onClick={toggleComplete}>Mark as complete</button>
</li>)}
</ul>
</div>
);
}
export default App;
CodePudding user response:
You can see that the toggleComplete
function takes a parameter i
which is the id of the todo, so you should call it like onClick={() => toggleComplete(todos.id)}
.
However this still didn't work since you are assigning random numbers as strings as id to the todos then iterating on the array.
As Alex pointed out there the bug in your code regarding the completed toggle, so I fixed it and here's a working version of the code you can take a look at and improve:
import React, { useState } from "react";
export default function App() {
const [todos, setTodos] = useState([]);
const toggleComplete = (i) => {
setTodos(
todos.map((todo, k) => {
return k === i
? {
...todo,
completed: !todo.completed
}
: todo;
})
);
};
const handleSubmit = (event) => {
event.preventDefault();
const todo = event.target[0].value;
setTodos((prevTodos) => {
return [
...prevTodos,
{
userTodo: todo,
completed: false,
id: prevTodos.length
}
];
});
};
return (
<div>
<form onSubmit={handleSubmit}>
<input placeholder="name"></input>
<button type="submit">submit</button>
</form>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{todo.completed ? (
<s>
<p>{todo.userTodo}</p>
</s>
) : (
<p>{todo.userTodo}</p>
)}
<button onClick={() => toggleComplete(todo.id)}>
Mark as complete
</button>
</li>
))}
</ul>
</div>
);
}
CodePudding user response:
There are 2 problems in your code as i see:
- typo in the
toggleComplete
function
Fix: the following code complete: !todo.complete
shopuld be completed: !todo.completed
as this is the name of the key that you're setting below on handleSubmit
.
- the
toggleComplete
function receives as an argument the javascriptevent
object and you are comparing it with thekey
here:(todo, k) => k === i
(see more here: https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event)
Fix: You can modify the lines of code for the todo render as follows:
{todos.map((todo, index) => <li key={todo.id}>
<React.Fragment>{
todo.completed ? <del><h4>{todo.userTodo}</h4></del> : <h4>{todo.userTodo}</h4>}
</React.Fragment>
<button onClick={() => {toggleComplete(index)}}>Mark as complete</button>
</li>)}