0

I have this code

const [selectedStudents, setSelectedStudents] = useState([])
const studentsChangeHandler = (e) => {
  setSelectedStudents({...selectedStudents,[e.target.id]:e.target.value});
  console.log('selectedStudents is ',selectedStudents)
};

and on the website

<input
  type="checkbox"
  id={index}
  value={student._id}
  onChange={studentsChangeHandler}
/>

And when I select the first checkbox on the site, nothing is written, but the second one is written, why?

first

second

6
  • 1
    Because setSelectedStudents is not synchronous. So, the state changes you can see on the next render. That's why in the console.log you are seeing the previous state. Commented Apr 17, 2021 at 12:37
  • @Nithish so everything works? Commented Apr 17, 2021 at 12:41
  • as a result, I get the object this way, can I somehow make it a pure array? @Nithish Commented Apr 17, 2021 at 12:42
  • Yes, everything should work. You can use useEffect in order to see the changes. Commented Apr 17, 2021 at 12:43
  • 1
    The reason why it's storing as object is you are passing an object to setSelectedStudents in studentsChangeHandler. instead you should add the new id. That's the coding mistake I can say. Commented Apr 17, 2021 at 12:44

2 Answers 2

1

In general setState or useState in React is asynchronous. So, the updated state we'll not be able to capture in the as soon as we update the state.

In hooks, the updated state will be available in the next render only. So, in order to see if the state is getting updated or not, we can make use of useEffect hook. Below is the example for the same.

const { useState, useEffect } = React;

const App = () => {
  const [students, setStudents] = useState([]);
  
  const onChangeHandler = event => {
    const {target: {id, value}} = event;
    setStudents(prevStudents => {
      //Push the new object with the id as key and value 
      //of the checkbox as value in the array.
      //In your case the value will be the student id. 
      //But here for simplicity I have used 1, 2 as ids & values
      return [...prevStudents, {[id]: value}]
    });
  }
  
  //To print the initial state as well as whenever the state(i.e., students gets updated
  useEffect(() => {
    console.log(students);
  }, [students])
  
  return (
   <div>
    <div>
      <input type="checkbox" id="1" value="1" onChange={onChangeHandler}/>
      <span>Input 1</span>
    </div>
    <div> 
     <input type="checkbox" id="2" value="2" onChange={onChangeHandler}/>
     <span>Input 2</span>
    </div>
   </div>
  )
}

ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

<div id="react"></div>

Note: For the demo purpose I have not considered removing the objects from the state when the checkbox is getting unchecked. I'm just adding a new object with id as key and value as value in the array irrespective of the state of the checkbox. So, please update the onChangeHandler logic as per your needs.

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

Comments

0

You can do it with jquery. This example selects the checked checkboxes by a selector and then calls the .each() method to each element and finally push them into the array.

<!DOCTYPE html>
<html>
  
<head>
    <title>
        JQuery | Get all selected checkboxes in an array.
    </title>
    <style>
        #GFG_UP {
            font-size: 17px;
            font-weight: bold;
        }
          
        #GFG_DOWN {
            color: green;
            font-size: 24px;
            font-weight: bold;
        }
          
        button {
            margin-top: 20px;
        }
    </style>
</head>
<script src=
    "https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js">
</script>
  
<body style="text-align:center;" id="body">
    <h1 style="color:green;">  
            Geek
        </h1>
    <p id="GFG_UP">
    </p>
      
    <input type="checkbox" name="type" value="GFG" /> GFG:
    <input type="checkbox" name="type" value="Geeks" /> Geeks:
    <input type="checkbox" name="type" value="Geek" /> Geek:
    <input type="checkbox" name="type" value="portal" /> portal:
    <br>
    <button>
        click here
    </button>
    <p id="GFG_DOWN">
    </p>
    <script>
        $('#GFG_UP')
        .text('First check few elements then click on the button.');
        $('button').on('click', function() {
            var array = [];
            $("input:checkbox[name=type]:checked").each(function() {
                array.push($(this).val());
            });
            $('#GFG_DOWN').text(array);
        });
    </script>
</body>
  
</html>

1 Comment

this will work, but isn't it better to avoid it in React?

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.