kb_
kb_

Reputation: 187

How to count selected checkboxes in React functional component?

I am needing to add bit of text in the sidebar of the following code. In each section, I need to have a count of the number of checkboxes selected. Is it at all possible to do this in a functional component as I have? Any examples that I have found so far are only for class components. I would like to keep it as a functional component if possible.

I have the code below, but here is a working version too:

https://codesandbox.io/s/react-playground-forked-jgmof?file=/index.js

import React, { useState } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { categoryData } from "./data";

const Menu = (props) => {
  const [showPanel, togglePanel] = useState(false);

  return (
    <MenuWrapper>
      <p>Showing 20 results</p>

      <div className="button-wrapper">
        <p>Filter By</p>

        {categoryData.map((categorybutton, i) => (
          <div key={i}>
            <button key={i} onClick={() => togglePanel(!showPanel)}>
              {categorybutton.category}
            </button>
          </div>
        ))}

        <div>
          {showPanel && (
            <Sidebar>
              {categoryData.map((categorysection, i) => (
                <details className="dropdown-header">
                  <summary>{categorysection.category}</summary>

                  {categorysection.data.map((categorylabel, i) => (
                    <div key={i}>
                      <input
                        type="checkbox"
                        id="vehicle1"
                        name="vehicle1"
                        value="Bike"
                      />
                      <label for="vehicle1">{categorylabel.label}</label>
                    </div>
                  ))}
                </details>
              ))}
            </Sidebar>
          )}
        </div>

        <p>Toggle</p>
        <p>Clear all filters</p>
      </div>
    </MenuWrapper>
  );
};

const MenuWrapper = styled.div`
  width: 90vw;
  display: flex;
  justify-content: space-between;

  .button-wrapper {
    display: flex;
  }
`;

const Sidebar = styled.div`
  position: fixed;
  width: 200px;
  height: 100vh;
  background: white;
  top: 0;
  left: 0;
  z-index: 100000;
  text-align: left;

  .dropdown-header {
    background: #f7f7f7;
    margin: 20px;
  }
`;

ReactDOM.render(<Menu />, document.getElementById("container"));
export const categoryData = [
  {
    category: "user",
    data: [
      { checked: false, value: "Me", label: "Me" },
      { checked: false, value: "Kids", label: "Kids" },
      { checked: false, value: "Guestroom", label: "Guestroom" }
    ]
  },
  {
    category: "comfort",
    data: [
      { checked: false, value: "Ultra-Soft", label: "Ultra-Soft" },
      { checked: false, value: "Soft", label: "Soft" },
      { checked: false, value: "Medium", label: "Medium" },
      { checked: false, value: "Medium-Firm", label: "Medium-Firm" },
      { checked: false, value: "Firm", label: "Firm" },
      { checked: false, value: "Ultra-Firm", label: "Ultra-Firm" },
      { checked: false, value: "Unsure", label: "Unsure" }
    ]
  },
  {
    category: "type",
    data: [
      { checked: false, value: "Pillow Top", label: "Pillow Top" },
      { checked: false, value: "Open Coil", label: "Open Coil" },
      { checked: false, value: "Pocketed Coil", label: "Pocketed Coil" },
      { checked: false, value: "Quantum Coil", label: "Quantum Coil" },
      { checked: false, value: "Memory Foam", label: "Memory Foam" },
      { checked: false, value: "Latex", label: "Latex" },
      { checked: false, value: "Unsure", label: "Unsure" }
    ]
  },
  {
    category: "budget",
    data: [
      { checked: false, value: 500, label: "Under $500" },
      { checked: false, value: 1000, label: "$501 - $1000" },
      { checked: false, value: 1600, label: "1001 - $1600" },
      { checked: false, value: 2500, label: "$1601 - $2500" },
      { checked: false, value: 2501, label: "$2500 and up" },
      { checked: false, value: "Unsure", label: "Unsure" }
    ]
  }
];

Upvotes: 1

Views: 2680

Answers (1)

bertdida
bertdida

Reputation: 5288

If I understand correctly, you wan't to show the total number of selected items on each category.

It would be easy if we'll create a new component for the section that will have it's own state tracking its selected items.

Let's call it CategorySection. It would then have a selected state that will be an array (empty by default) of its selected items. To update our selected state, we have to fireup a function everytime any of the checkbox is changed — if the checkbox is checked, we add the current item to our selected state otherwise it will be removed.

Then to display the total selected items, we can simply count the length of our selected state.

function CategorySection({ categorysection }) {
  const [selected, setSelected] = useState([]);

  function onChange(event, item) {
    if (event.target.checked) {
      setSelected([...selected, item]);
    } else {
      setSelected((prev) =>
        prev.filter((currItem) => currItem.value !== item.value)
      );
    }
  }

  return (
    <details className="dropdown-header">
      <summary>
        {categorysection.category}{" "}
        {selected.length > 0 ? selected.length : null}
      </summary>

      {categorysection.data.map((categorylabel, i) => (
        <div key={i}>
          <input
            type="checkbox"
            id={categorylabel.value}
            name="vehicle1"
            value="Bike"
            onChange={(event) => onChange(event, categorylabel)}
          />
          <label for={categorylabel.value}>{categorylabel.label}</label>
        </div>
      ))}
    </details>
  );
}

Edit React PlayGround (forked)

PS: You should have a unique id for every checkbox on your page.

Upvotes: 2

Related Questions