Home > OS >  Checkbox filtering on object property - react
Checkbox filtering on object property - react

Time:09-19

So i have some dummy data and want to filter through array and display objects that have that specific status.

Currently when i click on some checkbox it filters objects with clicked status correctly but then from what i understand - filtered data gets saved to invoiceList state so unchecking it has zero sense, because next filtering is based on that filtered objects previously.

I also want to combine checked checkboxes so it filters objects with both e.g. "paid" and "pending" statutes.

How to do all of these filerings properly?

const {useState, useEffect} = React;

const DATA = [
  {
    name: "invoice1",
    status: "paid"
  },
  {
    name: "invoice2",
    status: "paid"
  },
  {
    name: "invoice3",
    status: "pending"
  },
  {
    name: "invoice4",
    status: "draft"
  }
];

const App = () => {
  const [invoiceList, setInvoiceList] = useState([]);

  useEffect(() => {
    setInvoiceList(DATA);
  }, []);

  const statuses = ["draft", "pending", "paid"];
  const [checkedState, setCheckedState] = useState(
    new Array(statuses.length).fill(false)
  );

  const handleCheckboxChange = (position, status) => {
    const updatedCheckedState = checkedState.map((item, index) =>
      index === position ? !item : item
    );
    setCheckedState(updatedCheckedState);

    //THIS
    ////////////////////////////////////////////
    const filteredList = invoiceList.filter(
      (invoice) => invoice.status === status
    );
    setInvoiceList(filteredList);
  };

  return (
    <div>
      <div>
        {statuses.map((status, index) => (
          <label key={index}>
            <input
              type="checkbox"
              checked={checkedState[index]}
              onChange={(e) => handleCheckboxChange(index, status)}
            />
            <span>{status}</span>
          </label>
        ))}
      </div>

      <ul>
        {invoiceList.map((invoice,index) => {
          return <li key={index}>{invoice.name}</li>;
        })}
      </ul>
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById("root")
).render(<App/>);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

CodePudding user response:

Instead of saving the state using setInvoiceList, you may filter the list using useMemo hook.

const {useState, useEffect, useMemo} = React;

const DATA = [
  {
    name: "invoice1",
    status: "paid"
  },
  {
    name: "invoice2",
    status: "paid"
  },
  {
    name: "invoice3",
    status: "pending"
  },
  {
    name: "invoice4",
    status: "draft"
  }
];

const App = () => {
  const [invoiceList, setInvoiceList] = useState([]);

  useEffect(() => {
    setInvoiceList(DATA);
  }, []);

    const statuses = ["draft", "pending", "paid"];

    // keep track of the selected/active status
    const [activeStatus, setActiveStatus] = useState();

    const filteredInvoices = useMemo(() => {
        // if no active status, return all invoices
        if (!activeStatus) {
            return invoiceList;
        }

        // otherwise, filter invoices by active status
        return invoiceList.filter(invoice => invoice.status === activeStatus);

    },[activeStatus, invoiceList]);
 

  return (
    <div>
      <div>
        {statuses.map((status, index) => (
          <label key={index}>
            <input
              type="checkbox"
              checked={statuses[index] === activeStatus}
              onChange={(e) => setActiveStatus(status === activeStatus ? undefined : status)}
            />
            <span>{status}</span>
          </label>
        ))}
      </div>

      <ul>
        {filteredInvoices.map((invoice,index) => {
          return <li key={index}>{invoice.name}</li>;
        })}
      </ul>
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById("root")
).render(<App/>);

For multiple status support, you can use the following

const {useState, useEffect, useMemo} = React;

const DATA = [
  {
    name: "invoice1",
    status: "paid"
  },
  {
    name: "invoice2",
    status: "paid"
  },
  {
    name: "invoice3",
    status: "pending"
  },
  {
    name: "invoice4",
    status: "draft"
  }
];

const App = () => {
  const [invoiceList, setInvoiceList] = useState([]);

  useEffect(() => {
    setInvoiceList(DATA);
  }, []);

    const statuses = ["draft", "pending", "paid"];

    // keep track of the selected/active statuses
    const [activeStatuses, setActiveStatuses] = useState([]);

    // toggle the status
    const toggleStatus = (status) => {
        if (activeStatuses.includes(status)) {
            setActiveStatuses(activeStatuses.filter((s) => s !== status));
        } else {
            setActiveStatuses([...activeStatuses, status]);
        }
    };

    // filter the invoices based on the active statuses
    const filteredInvoices = useMemo(() => {

        // if there are no active statuses, return the original list
        if (activeStatuses.length === 0) {
            return invoiceList;
        }

        // otherwise, filter the list
        return invoiceList.filter(invoice => activeStatuses.includes(invoice.status));

    },[activeStatuses, invoiceList]);
 

  return (
    <div>
      <div>
        {statuses.map((status, index) => (
          <label key={index}>
            <input
              type="checkbox"
              checked={activeStatuses.includes(status)}
              onChange={(e) => toggleStatus(status)}
            />
            <span>{status}</span>
          </label>
        ))}
      </div>

      <ul>
        {filteredInvoices.map((invoice,index) => {
          return <li key={index}>{invoice.name}</li>;
        })}
      </ul>
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById("root")
).render(<App/>);
  • Related