Home > other >  Re-Rendering a component
Re-Rendering a component

Time:01-05

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>
  );
}

CodePudding user response:

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.

CodePudding user response:

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!

CodePudding user response:

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} />
  •  Tags:  
  • Related