I am making a todo list and in the following Tasks component i have an li component with mapped data i want to conditionally add a strikethrough on the task for which i used a useState hook with intial property as null. I then added onClick functions to change state to true and false respectively but my style is not being applied :( Below is the code for the same
`import React, {useState} from 'react';
import "./Tasks.css";
import DoneIcon from '@mui/icons-material/Done';
import CloseIcon from '@mui/icons-material/Close';
function Tasks(props) {
const [done, setDone] = useState(null);
return (
<div className="tasks">
{props.items.map(item => (
<li key={item.id} onClick={() => setDone(false)} style={{textDecorationLine: done && 'line-through'}}>{item.text}
<div>
<button onClick={() => setDone(true)} style={{color: "green"}}><DoneIcon/></button>
<button style={{color: "red"}}><CloseIcon/></button>
</div>
</li>
))}
</div>
)
}
export default Tasks;`
I am making a todo list and in the following Tasks component i have an li component with mapped data i want to conditionally add a strikethrough on the task for which i used a useState hook with intial property as null. I then added onClick functions to change state to true and false respectively but my style is not being applied :( Here is the code for the same
CodePudding user response:
Your code has a couple of issues:
- If you want to save the state for each task, you can't use a single boolean. You need and array or object that holds one value per task.
- When you click your Done button, both
onClick
callbacks are triggered (the one for<button>
and the one for<li>
), setting the value ofdone
totrue
and immediately tofalse
again.
To solve [1], you can make your done
variable an object, where each key is an item id and matching value is a boolean that determines if the task is done.
To solve [2], remove onClick
from the <li>
. You can still achieve a toggle effect if you set the task state based on previous state, in the button's onClick
.
The following code should be what you need:
// done is an object that maps task ids to booleans
const [done, setDone] = useState({});
return (
<div className="tasks">
{props.items.map(item => (
<li key={item.id} style={{textDecorationLine: done[item.id] && 'line-through'}}>{item.text}
<div>
<button onClick={() => setDone(prevState => ({...prevState, [item.id]: !prevState[item.id]}))} style={{color: "green"}}><DoneIcon/></button>
<button style={{color: "red"}}><CloseIcon/></button>
</div>
</li>
))}
</div>
)
CodePudding user response:
You need to save the item.id
to determine which item was clicked, not a boolean.
import React, {useState} from 'react';
import "./Tasks.css";
import DoneIcon from '@mui/icons-material/Done';
import CloseIcon from '@mui/icons-material/Close';
function Tasks(props) {
const [taskWasDone, setTaskWasDone] = useState(null);
return (
<div className="tasks">
{props.items.map(item => (
<li key={item.id} onClick={() => setTaskWasDone(null)} style={{textDecorationLine: taskWasDone === item.id && 'line-through'}}>{item.text}
<div>
<button onClick={() => setTaskWasDone(item.id)} style={{color: "green"}}><DoneIcon/></button>
<button style={{color: "red"}}><CloseIcon/></button>
</div>
</li>
))}
</div>
)
}
export default Tasks;