How to get multiple checkbox values in React JS?

Viewed 17037

I'm trying to get values from all selected checkboxes and store them in useState. I was able to get value of the selected row in a table, but I can't store multiple selected values in state. My code is

import React, { useState } from "react";
export default function App() {
  const data = [
    {
      id: "1",
      name: "Jane",
      lastName: "Doe",
      age: "25"
    },
    {
      id: "2",
      name: "James",
      lastName: "Doe",
      age: "40"
    },
    {
      id: "3",
      name: "Alexa",
      lastName: "Doe",
      age: "27"
    },
    {
      id: "4",
      name: "Jane",
      lastName: "Brown",
      age: "40"
    }
  ];

  const [peopleInfo, setPeopleInfo] = useState([
    {
      id: "",
      first: "",
      last: "",
      age: ""
    }
  ]);

  console.log(peopleInfo);
  return (
    <div className="App">
      <table>
        <tr>
          {data.map((item) => {
            return (
              <div
                key={item.id}
                style={{
                  display: "flex",
                  width: "150px"
                }}
              >
                <input
                  onChange={() => {
                    setPeopleInfo({
                      id: item.id,
                      first: item.first,
                      last: item.last,
                      age: item.age
                    });
                  }}
                  value={peopleInfo}
                  style={{ margin: "20px" }}
                  type="checkbox"
                />
                <td style={{ margin: "20px" }}>{item.name}</td>
                <td style={{ margin: "20px" }}>{item.lastName}</td>
                <td style={{ margin: "20px" }}>{item.age}</td>
              </div>
            );
          })}
        </tr>
      </table>
    </div>
  );
}

and Codesandbox

I'm able to get id, name, last name and age values of the selected row, but when I select new checkbox previous row's info gets updated with the new one. I can't seem to figure out how to store all values from multiple selected checkboxes. Any help and suggestions are greatly appreciated.

4 Answers

It's ok to have an array containing your selected people.

const [peopleInfo, setPeopleInfo] = useState([]);

what you want to do is using e.target.checked value to modify the array above.

<input
  onChange={(e) => {
    // add to list
    if (e.target.checked) {
      setPeopleInfo([
        ...peopleInfo,
        {
          id: item.id,
          first: item.name,
          last: item.lastName,
          age: item.age,
        },
      ]);
    } else {
      // remove from list
      setPeopleInfo(
        peopleInfo.filter((people) => people.id !== item.id),
      );
    }
  }}
  value={peopleInfo}
  style={{ margin: '20px' }}
  type="checkbox"
/>

And the results can be accessed from peopleInfo.

I would suggest storing your selected item objects in an object using the item.id as a key. This will provide you constant-time (O(1)) value lookups, versus linear-time (O(n)) using an array. If/when you need an array of the checked values you access via Object.values(peopleInfo).

const [peopleInfo, setPeopleInfo] = useState({});

And use a functional state update to copy all previous state into a new object and update the value associated with the specific item's id. Make sure to use the checkbox input's checked prop instead of the value prop.

<input
  onChange={() => {
    setPeopleInfo((state) => ({
      ...state, // <-- shallow copy previous state
      [item.id]: state[item.id] // <-- update value by id
        ? null
        : {
        id: item.id,
        first: item.name, // <-- use name property
        last: item.lastName, // <-- use lastName property
        age: item.age
      }
    }));
  }}
  checked={peopleInfo[item.id]} // <-- use checked prop, retrieve value by id
  style={{ margin: "20px" }}
  type="checkbox"
/>

Edit how-to-get-multiple-checkbox-values-in-react-js

Full Code:

export default function App() {
  const [peopleInfo, setPeopleInfo] = useState({});

  useEffect(() => {
    console.log(peopleInfo);
  }, [peopleInfo]);

  return (
    <div className="App">
      <table>
        <tr>
          {data.map((item) => {
            return (
              <div
                key={item.id}
                style={{
                  display: "flex",
                  width: "150px"
                }}
              >
                <input
                  onChange={() => {
                    setPeopleInfo((state) => ({
                      ...state,
                      [item.id]: {
                        id: item.id,
                        first: item.name,
                        last: item.lastName,
                        age: item.age
                      }
                    }));
                  }}
                  value={peopleInfo[item.id]}
                  style={{ margin: "20px" }}
                  type="checkbox"
                />
                <td style={{ margin: "20px" }}>{item.name}</td>
                <td style={{ margin: "20px" }}>{item.lastName}</td>
                <td style={{ margin: "20px" }}>{item.age}</td>
              </div>
            );
          })}
        </tr>
      </table>
    </div>
  );
}

Callback Optimization

Factor out the onChange callback logic into a function that consumes the item you want to toggle the checkbox state of. I suggest using curried functions so you enclose the callback value and can avoid the unnecessary anonymous callback.

const toggleHandler = (item) => () => {
  setPeopleInfo((state) => ({
    ...state,
    [item.id]: state[item.id]
      ? null
      : {
          id: item.id,
          first: item.name,
          last: item.lastName,
          age: item.age
        }
  }));
};

...

<input
  onChange={toggleHandler(item)} // <-- pass item to handler
  checked={peopleInfo[item.id]}
  style={{ margin: "20px" }}
  type="checkbox"
/>

You can create peopleInfo state as a empty array:

const [peopleInfo, setPeopleInfo] = useState([]);

then in your onChange function of your checkbox handle the object array:

onChange={() => {
   handleChange(item);             
}}

const handleChange = (item) => {
   let tempPeopleInfo = peopleInfo;
   //here we look if your checked item is allready in your array. if so, remove with filter.
   if (tempPeopleInfo.some((people) => people.id === item.id)) {
     tempPeopleInfo = tempPeopleInfo.filter((people) => people.id !== item.id);
   } else {
     tempPeopleInfo.push(item);
   }
   setPeopleInfo(tempPeopleInfo);
};

https://codesandbox.io/s/upbeat-lederberg-hk6cd?file=/src/App.js

One of nice approach, try this. We can manage easily based on checked true/false. While send BE also we can filter it out by checked.

import "./styles.css";

import React, { useState } from "react";
export default function App() {
  
  const [peopleInfos, setPeopleInfo] = useState([
    {
      id: "1",
      name: "Jane",
      lastName: "Doe",
      age: "25",
      checked:false
    },
    {
      id: "2",
      name: "James",
      lastName: "Doe",
      age: "40",
      checked:false
    },
    {
      id: "3",
      name: "Alexa",
      lastName: "Doe",
      age: "27",
      checked:false
    },
    {
      id: "4",
      name: "Jane",
      lastName: "Brown",
      age: "40",
      checked:false
    }
  ]);
  const peopleChecked=(event)=>{
    setPeopleInfo(peopleInfos.map((info)=>{
     return {...info, checked:event.target.checked};
    }));

   // or
   
    // const people = [...peopleInfos];
    // people[index] = {...people[index], checked:event.target.checked};
    // setPeopleInfo([...people]);
  }
  

  console.log(peopleInfos);
  return (
    <div className="App">
      <table>
        <tr>
          {peopleInfos.map((item, index) => {
            return (
              <div
                key={item.id}
                style={{
                  display: "flex",
                  width: "150px"
                }}
              >
                <input
                  onChange={(event)=>peopleChecked(event, index)}
                  name="people"
                  value={item.checked}
                  style={{ margin: "20px" }}
                  type="checkbox"
                />
                <td style={{ margin: "20px" }}>{item.name}</td>
                <td style={{ margin: "20px" }}>{item.lastName}</td>
                <td style={{ margin: "20px" }}>{item.age}</td>
              </div>
            );
          })}
        </tr>
      </table>
    </div>
  );
}
Related