updating a state inside a map, causes to push new elements to state that I don't want

37 Views Asked by At

here I have data state which I'm trying to loop over it with map function, and print out checklist which I called myChecklist.

import React, { useState } from 'react'

const App = () => {

  const [data, setData] = useState([
    {
      id:0,
      isChecked: true
    },
    {
      id:1,
      isChecked: false
    },
    {
      id:2,
      isChecked: false
    },
  ])
  

  const myCheckList = data.map((checkbox, index) => {
    return (
      <div key={index}>
        <input type="checkbox" 
          checked={checkbox.isChecked} 
          onChange={e => {setData(prev => [...prev, prev[index].isChecked = e.target.checked])}} 
          id={index} />
        <label htmlFor={index}>{checkbox.id}</label>
      </div>
    )
  })

  console.log(data)

  return (
    <div>{myCheckList}</div>
  )
}

export default App

every time I click my any of the checkboxes, it updates to isChecked Boolean referring to that checkbox. the problem is pushes new elements to the data state which cause the to map function to create a new checkbox I don't want. when ever I click the my checkboxes the log of **data **state should look like this:

[
    {
        "id": 0,
        "isChecked": true
    },
    {
        "id": 1,
        "isChecked": false
    },
    {
        "id": 2,
        "isChecked": false
    }
]

but after changing the checkbox a couple of times it would look like this:

[
    {
        "id": 0,
        "isChecked": true
    },
    {
        "id": 1,
        "isChecked": false
    },
    {
        "id": 2,
        "isChecked": false
    },
    false,
    true
]

noticed some true false values pushed to the state. I am NOT pushing anyThing so why this happens ????

I'll appreciate your help. thanks

I tried to separate the state that I am mapping throw and the state I want to update like this

import React, { useState } from 'react'

const App = () => {

  const [isChecked, setIsChecked] = useState([
    {isChecked: false},
    {isChecked: false},
    {isChecked: false}
  ])

  const data = [
    {
      id:0,
    },
    {
      id:1,
    },
    {
      id:2,
    },
  ]


  const myCheckList = data.map((checkbox, index) => {
    return (
      <div key={index}>
        <input type="checkbox" 
          checked={isChecked[index].isChecked} 
          onChange={e => {setIsChecked(prev => [...prev, prev[index].isChecked = e.target.checked])}} 
          id={index} />
        <label htmlFor={index}>{checkbox.id}</label>
      </div>
    )
  })

  console.log(isChecked)

  return (
    <div id='my-id'>{myCheckList}</div>
  )
}

export default App```

no new checkboxes would be printed to DOM but the issue of pushing elements I don't to the state, still remains
1

There are 1 best solutions below

2
On BEST ANSWER
[
    {
        "id": 0,
        "isChecked": true
    },
    {
        "id": 1,
        "isChecked": true
    },
    {
        "id": 2,
        "isChecked": false
    }
]

If you want an output like this, there is a problem with your code. Particularly this line

          onChange={e => {setData(prev => [...prev, prev[index].isChecked = e.target.checked])}} 

What are you doing here is, you are appending to prev array, and the object that you are entering is a bool (whose logic is wrong).

Correct way would be to first determing the index of check box which is changed, and then flip is isChecked value.

Here is the code:

import React, { useState } from "react";

const App = () => {
  const [data, setData] = useState([
    {
      id: 0,
      isChecked: true,
    },
    {
      id: 1,
      isChecked: false,
    },
    {
      id: 2,
      isChecked: false,
    },
  ]);
  const handleCheckboxChange = (index) => {
    setData((prev) => {
      return prev.map((checkbox, i) => {
        if (i === index) {
          return {
            ...checkbox,
            isChecked: !checkbox.isChecked,
          };
        }
        return checkbox;
      });
    });
  };

  const myCheckList = data.map((checkbox, index) => {
    return (
      <div key={index}>
        <input
          type="checkbox"
          checked={checkbox.isChecked}
            onChange={() => handleCheckboxChange(index)}
          id={index}
        />
        <label htmlFor={index}>{checkbox.id}</label>
      </div>
    );
  });

  console.log(data);

  return <div>{myCheckList}</div>;
};

export default App;