1

I am making a react app where I need to populate dynamic checkboxes from the data.

  const sectors = [
    {
      SectorID: 5,
      Name: "Wood Truss/Panel/Building Components"
    },
    {
      SectorID: 33,
      Name: "LBM Retail/Pro Contractor"
    },
    {
      SectorID: 24,
      Name: "Light Gauge Steel Truss/Panel"
    }
  ];

Then I do have a list of selected checkboxes here,

  const selectedSectors = [
    {
      SectorID: 5
    },
    {
      SectorID: 24
    }
  ];

So here using SectorID, we can find a match.

Expected result: Need to make the selected sector to be checked on load.

Solution I tried:

  const [checkedItems, setCheckedItems] = useState({});

  React.useEffect(() => {
    const newData = sectors.map((sector) => {
      return selectedSectors.map((selected) => {
        if (selected.SectorId === sector.SectorId) {
          setCheckedItems((prev) => {
            const checkedItems = {
              ...prev.checkedItems,
              [sector.Name]: true
            };
            return { ...prev, checkedItems };
          });
        }
      });
    });
  }, []);

The above one doesn't make the selected sectors checked and I think I am doing something wrong here.

Populating checkboxes like:

      {sectors &&
        sectors.map((sector, index) => (
          <React.Fragment key={index}>
            <label className="block mr-4">
              <input
                className="mx-2 leading-tight"
                name={sector.Name}
                checked={checkedItems[sector.Name]}
                onChange={(e) => handleInputChange(e, sector)}
                type="checkbox"
              />
              <span className="text-sm">{sector.Name}</span>
            </label>
          </React.Fragment>
        ))}

Codesandbox:

Edit React Functional Component (forked)

Requirement: Please help me to make the checkboxes get checked by default on comparing with selectedSectors..

Undefined
  • 851
  • 5
  • 20
  • 48

3 Answers3

2

If you don't need to do anything with the checked state then you should use the defaultChecked attribute and simply search the selectedSectors array for a match.

defaultChecked={selectedSectors.some(
  (selectedSector) =>
    selectedSector.SectorID === sector.SectorID
)}

If you want to keep your checkedItems state then you can initialize your state in the effect. Iterate over the sectors array and reduce into an object of sector ids to selected values

React.useEffect(() => {
  const defaultCheckedSectors = sectors.reduce(
    (defaultChecked, sector) => ({
      ...defaultChecked,
      [sector.SectorID]: selectedSectors.some(
        (selectedSector) => selectedSector.SectorID === sector.SectorID
      )
    }),
    {}
  );
  setCheckedItems(defaultCheckedSectors);
}, []);

Update the checked attribute to use the SectorId.

<input
  className="mx-2 leading-tight"
  name={sector.Name}
  checked={checkedItems[sector.SectorID]} // <-- use SectorID
  onChange={(e) => handleInputChange(e, sector)}
  type="checkbox"
/>

You will need to also update handleInputChange to consume the sector. Alternatively you could also just simply pass the SectorID value.

const handleInputChange = (event, sector) => { // <-- consume sector
  const { checked } = event.target;
  setCheckedItems(prev => ({
    ...prev,
    [sector.SectorID]: checked // <-- use sector for ID
  }))
};

Edit how-to-make-the-checkbox-checked-default

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • I need to check the checked values and pass it down to ```handleInputChange``` to make another set of actions.. I will display another set of data based on the selected sectors.. – Undefined Oct 29 '20 at 08:10
  • Can you provide codesandbox example of your solution please? – Undefined Oct 29 '20 at 08:13
  • @Undefined Sure... I was in the middle of adding the version if you needed to keep the checked state and added the link to sandbox. – Drew Reese Oct 29 '20 at 08:17
  • Could you please help me here? https://stackoverflow.com/q/64604518/13270726 – Undefined Oct 30 '20 at 08:03
1

You have no need to create another array with checked items. Just change your input like-

<input className="mx-2 leading-tight"
    name={sector.Name}
    checked={!!selectedSectors.find(item => item.SectorID === sector.SectorID)}
    onChange={(e) => handleInputChange(e, sector)}
    type="checkbox" />
Sajeeb Ahamed
  • 6,070
  • 2
  • 21
  • 30
0

This whole approach isn't quite the React way to do it. There should be a single source of truth in state that tracks the sectors' values AND if they're checked. And the initial state for useState should be used to determine the initially checked status.

See this code sandbox I forked from yours to see it rewritten in a more React-y way

Jayce444
  • 8,725
  • 3
  • 27
  • 43
  • Why you have hard coded the data inside the ```useState```? I can't hard code anything like that.. – Undefined Oct 29 '20 at 08:14
  • ...because the data was hardcoded in your code? You can easily pass in a dynamic value, though that's a separate thing – Jayce444 Oct 29 '20 at 08:16
  • 2
    Not necessarily every aspect in React should be "controlled", so you cannot deem a piece of code as "not the react way" without knowing the whole story – vsync Oct 29 '20 at 08:16
  • I have hardcoded only the actual data which comes from api in real application.. – Undefined Oct 29 '20 at 08:17
  • @vsync I never said "everything in React should *always* be controlled". I was basing it purely off the question's use case and code provided, in which they wanted to track the checked status of a checkbox and render the UI accordingly. Which is when you use controlled components. Though I probably could've been a bit more specific – Jayce444 Oct 29 '20 at 08:24