React usestate array length varies depending on item i click

im trying to build a list you can add and delete components from.

Adding works but when i try to delete an item, every item that comes after that also gets deleted. I found that the length of the use state array i use varies depending on which item i click delete on.

const Alerting = () => {
const [Alerts, setAlert] = useState([]);

 const AddAlertingChoice = () => {
        const addedAlerts = Alerts => [...Alerts, <AlertingCoinChoice coins={coins} id={new Date().getUTCMilliseconds()}];
        setAlert(addedAlerts);
    }

    const DeleteAlertingChoice = id =>{

        console.log("alerts "+Alerts.length) //This length always displays the item index-1?

        const removedArr = [...Alerts].filter(alert => alert.props.id != id)
        setAlert(removedArr)
    }

 return (
        <>
            <AlertingContainer>
                <CoinChoiceContainer>
                    {Alerts.map((item, i) => (
                        item
                    ))}
                </CoinChoiceContainer>
                <AddAlertButton onClick={AddAlertingChoice}>+</AddAlertButton>
            </AlertingContainer>
        </>
    )

The items

const AlertingCoinChoice = ({coins, id, DeleteAlertingChoice}) => {

    return (
        <>
            <AlertingCoin>
                <CoinSelect id={'SelectCoin'}>
                    <OptionCoin value='' disabled selected>Select your option</OptionCoin>
                </CoinSelect>
                <ThresholdInput id={'LowerThresholdInput'} type='number'
                                pattern='^-?[0-9]d*.?d*$'/>
                <ThresholdInput id={'UpperThresholdInput'} type='number'
                                pattern='^-?[0-9]d*.?d*$'/>
                <SaveButton id={'AlertSaveAndEdit'} onClick={ClickSaveButton}>Save</SaveButton>
                <DeleteAlertButton onClick={() => {DeleteAlertingChoice(id)}}>X</DeleteAlertButton>
            </AlertingCoin>
        </>
    )

why cant it just delete the item i pass with the id parameter?

Answer

It sounds like you’re only passing down the DeleteAlertingChoice when initially putting the new <AlertingCoinChoice into state, so when it gets called, the length is the length it was when that component was created, and not the length the current state is.

This also causes the problem that the DeleteAlertingChoice that a component closes over will only have the Alerts it closes over from the time when that one component was created – it won’t have the data from further alerts.

These problems are all caused by one thing: the fact that you put the components into state. Don’t put components into state, instead transform state into components only when rendering.

const Alerting = () => {
   const [alerts, setAlerts] = useState([]);

   const AddAlertingChoice = () => {
      setAlerts([
         ...alerts,
         { coins, id: Date.now() }
      ]);
   }
   const DeleteAlertingChoice = id => {
      console.log("alerts " + alerts.length);
      setAlerts(alerts.filter(alert => alert.id !== id));
   }
   return (
         <AlertingContainer>
            <CoinChoiceContainer>
               {Alerts.map((alertData) => (
                  <AlertingCoinChoice {...alertData} DeleteAlertingChoice={DeleteAlertingChoice} />
               ))}
            </CoinChoiceContainer>
            <AddAlertButton onClick={AddAlertingChoice}>+</AddAlertButton>
         </AlertingContainer>
   );
};

You also don’t need fragments <> </> when you’re already only rendering a single top-level JSX item.