0

I'm doing a simple todo list using React. What I fail to do is to remove an item once I click on the button. However, if I click delete and then add a new item, it's working, but only if I add a new todo.

Edit:I've edited the post and added the parent componenet of AddMission.

    import React,{useState}from 'react';
import { Button } from '../UI/Button/Button';
import Card from '../UI/Card/Card';
import classes from '../toDo/AddMission.module.css'

const AddMission = (props) => {
const [done,setDone]=useState(true);
const doneHandler=(m)=>{
m.isDeleted=true;
}

  return (
   <Card className={classes.users}>
      <ul>
        {props.missions.map((mission) => (
        <li className={mission.isDeleted?classes.done:''}  key={mission.id}>
                 {mission.mission1} 
          <div className={classes.btn2}>
           <Button onClick={()=>{
             doneHandler(mission)
           }} className={classes.btn}>Done</Button>
           </div> 
          </li>  
        )) }
      </ul>
    </Card>
  );
};

export default AddMission;

import './App.css';
import React,{useState} from 'react';
import { Mission } from './components/toDo/Mission';
import AddMission from './components/toDo/AddMission';
function App() {
  const [mission,setMission]=useState([]);
  const [isEmpty,setIsEmpty]=useState(true);
  const addMissionHandler = (miss) =>{
    setIsEmpty(false);
    setMission((prevMission)=>{
      return[
        ...prevMission,
        {mission1:miss,isDeleted:false,id:Math.random().toString()},
      ];
    });
  };
  return (
    <div className="">
    <div className="App">
    <Mission onAddMission={addMissionHandler}/>
    {isEmpty?<h1 className="header-title">Start Your Day!</h1>:(<AddMission isVisible={mission.isDeleted} missions={mission}/>)}
    </div>
    </div>
  );
}
1
  • use Immutables to capture the changes or try to save the missions in the state and then perform delete on it by changing the state. Commented Jan 4, 2022 at 16:50

3 Answers 3

1
const doneHandler=(m)=>{
    m.isDeleted=true;
}

This is what is causing your issue, you are mutating an object directly instead of moving this edit up into the parent. In react we don't directly mutate objects because it causes side-effects such as the issue you are having, a component should only re-render when its props change and in your case you aren't changing missions, you are only changing a single object you passed in to your handler.

Because you haven't included the code which is passing in the missions props, I can't give you a very specific solution, but you need to pass something like an onChange prop into <AddMission /> so that you can pass your edited mission back.

You will also need to change your function to something like this...

const doneHandler = (m) =>{
  props.onChange({
    ...m,
    isDeleted: true,
  });
}

And in your parent component you'll then need to edit the missions variable so when it is passed back in a proper re-render is called with the changed data.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, and I've edited the post,I'm trying to do as you suggested
0

Like others have mentioned it is because you are not changing any state, react will only re-render once state has been modified.

Perhaps you could do something like the below and create an array that logs all of the ids of the done missions?

I'm suggesting that way as it looks like you are styling the list items to look done, rather than filtering them out before mapping.

import React, { useState } from "react";
import { Button } from "../UI/Button/Button";
import Card from "../UI/Card/Card";
import classes from "../toDo/AddMission.module.css";

const AddMission = (props) => {
    const [doneMissions, setDoneMissions] = useState([]);

    return (
        <Card className={classes.users}>
            <ul>
                {props.missions.map((mission) => (
                    <li
                        className={
                            doneMissions.includes(mission.id)
                                ? classes.done
                                : ""
                        }
                        key={mission.id}
                    >
                        {mission.mission1}
                        <div className={classes.btn2}>
                            <Button
                                onClick={() => {
                                    setDoneMissions((prevState) => {
                                        return [...prevState, mission.id];
                                    });
                                }}
                                className={classes.btn}
                            >
                                Done
                            </Button>
                        </div>
                    </li>
                ))}
            </ul>
        </Card>
    );
};

export default AddMission;

Hope that helps a bit!

Comments

0
m.isDeleted = true;

m is mutated, so React has no way of knowing that the state has changed.

Pass a function as a prop from the parent component that allows you to update the missions state.

<Button
  onClick={() => {
    props.deleteMission(mission.id);
  }}
  className={classes.btn}
>
  Done
</Button>;

In the parent component:

const deleteMission = (missionId) => {
    setMissions(prevMissions => prevMissions.map(mission => mission.id === missionId ? {...mission, isDeleted: true} : mission))
}

<AddMission missions={mission} deleteMission={deleteMission} />

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.