React. Filter the result upon click the checkbox input

Viewed 56

This is my first time here. So i plan to filter the result (remove item that stocked=false / item with red name) upon click the checkbox input.

i have try filter it in the const filter by checking the toggle is true and false but failed.

Additionally i hope to shorten my code while make it easier to read and understand as i am still new to this. Thanks in advance. i got the project from react-beta btw but i dont want to follow the code cause i dont understand it. This is the link: https://beta.reactjs.org/learn/thinking-in-react

import {useState} from "react";
import { Container, Row, Col } from "react-bootstrap";
const items = [
  { category: "Fruits", price: "$1", stocked: true, name: "Apple" },
  { category: "Fruits", price: "$1", stocked: true, name: "Dragonfruit" },
  { category: "Fruits", price: "$2", stocked: false, name: "Passionfruit" },
  { category: "Vegetables", price: "$2", stocked: true, name: "Spinach" },
  { category: "Vegetables", price: "$4", stocked: false, name: "Pumpkin" },
  { category: "Vegetables", price: "$1", stocked: true, name: "Peas" },
];
export default function Sort1() {
  const [fruits, setFruits] = useState("");
  const [toggle, setToggle] = useState(false);
  const filter = items.filter((e) =>
    e.name.toLowerCase().includes(fruits.toLowerCase())
  );
  function togglefilter(e) {
    if (e.target.checked) {
      setToggle(true);
    } else {
      setToggle(false);
    }
    console.log("toggle is " + e.target.checked);
  }
  // filter2 to select only category fruits
  const filter2 = filter.filter((e) => (e.category === "Fruits" ? e : ""));
  // filter3 to select only category vege
  const filter3 = filter.filter((e) => (e.category === "Vegetables" ? e : ""));
  function Filtered() {
    return (
      <Container>
        <Row>
          <Col>Name</Col>
          <Col>Price</Col>
        </Row>
        {/* fruits filter */}
        {filter2.length == 0 ? (
          ""
        ) : (
          <>
            <Row>
              <Col>Fruits</Col>
            </Row>
            {filter2.map((e, index) => {
              return (
                <Row key={index}>
                  {" "}
                  <Col>
                    {e.stocked ? (
                      e.name
                    ) : (
                      <span style={{ color: "red" }}>{e.name}</span>
                    )}
                  </Col>
                  <Col>{e.price}</Col>
                </Row>
              );
            })}
          </>
        )}
        {/* vegetables filter */}
        {filter3.length == 0 ? (
          ""
        ) : (
          <>
            <Row>
              <Col>Vegetables</Col>
            </Row>
            {filter3.map((e, index) => {
              return (
                <Row key={index}>
                  {" "}
                  <Col>
                    {e.stocked ? (
                      e.name
                    ) : (
                      <span style={{ color: "red" }}>{e.name}</span>
                    )}
                  </Col>
                  <Col>{e.price}</Col>
                </Row>
              );
            })}
          </>
        )}
      </Container>
    );
  }
  return (
    <div
      style={{
        textAlign: "center",
        margin: "50px",
        border: "solid 1px black",
        width: "600px",
        minHeight: "300px",
      }}
    >
      <input
        value={fruits}
        onChange={(e) => {
          setFruits(e.target.value);
        }}
        type="text"
        placeholder="Search..."
        style={{ margin: "20px", marginBottom: "0" }}
      />
      <br />
      <h6 style={{ color: "red", fontSize: "10px" }}>
        *red indicate unavailable items
      </h6>
      <label>
        <input type="checkbox" checked={toggle} onChange={togglefilter} /> Only
        Show Products in stock
      </label>
      <p>Search: {fruits}</p>
      <Filtered />
    </div>
  );
}
2 Answers

You should filter your items conditionally. Whenever toggle state is true filter them based on stocked property.

// Execute the following line conditionally based on `toggle` state
const inStockItems = items.filter((item) => item.stocked)

You can create a filterObject and to apply filtering with useMemo. Also, consider using useMemo and useState hooks, they will prevent your components from unneded rerendering and reevaluations.

import { useMemo, useState } from "react";
import { Container, Row, Col } from "react-bootstrap";
import { items as all_items } from "./data";

export default function Sort1() {
  const [items, setItems] = useState(all_items);

  const [filterObject, setFilterObject] = useState({
    name: "",
    isStockedOnly: false
  });

  const filteredItems = useMemo(() => {
    if (!items || !filterObject) return [];
    let filtered = [...items];
    const { name, isStockedOnly } = filterObject;
    if (name && name.length > 0) {
      filtered = filtered.filter((x) =>
        x.name.toLowerCase().includes(name.toLowerCase())
      );
    }
    if (isStockedOnly) {
      filtered = filtered.filter((x) => x.stocked === isStockedOnly);
    }
    return filtered;
  }, [items, filterObject]);

  const filteredFruits = useMemo(() => {
    return filteredItems.filter((x) => x.category === "Fruits");
  }, [filteredItems]);

  const filteredVegetables = useMemo(() => {
    return filteredItems.filter((x) => x.category === "Vegetables");
  }, [filteredItems]);

  function togglefilter(e) {
    setFilterObject((prev) => ({ ...prev, isStockedOnly: e.target.checked }));
  }

  function Filtered() {
    return (
      <Container>
        <Row>
          <Col>Name</Col>
          <Col>Price</Col>
        </Row>
        {/* fruits filter */}
        {filteredFruits?.length > 0 && (
          <>
            <Row>
              <Col>Fruits</Col>
            </Row>
            {filteredFruits.map((e, index) => {
              return (
                <Row key={index}>
                  {" "}
                  <Col>
                    {e.stocked ? (
                      e.name
                    ) : (
                      <span style={{ color: "red" }}>{e.name}</span>
                    )}
                  </Col>
                  <Col>{e.price}</Col>
                </Row>
              );
            })}
          </>
        )}
        {/* vegetables filter */}
        {filteredVegetables?.length > 0 && (
          <>
            <Row>
              <Col>Vegetables</Col>
            </Row>
            {filteredVegetables.map((e, index) => {
              return (
                <Row key={index}>
                  {" "}
                  <Col>
                    {e.stocked ? (
                      e.name
                    ) : (
                      <span style={{ color: "red" }}>{e.name}</span>
                    )}
                  </Col>
                  <Col>{e.price}</Col>
                </Row>
              );
            })}
          </>
        )}
      </Container>
    );
  }
  return (
    <div
      style={{
        textAlign: "center",
        margin: "50px",
        border: "solid 1px black",
        width: "600px",
        minHeight: "300px"
      }}
    >
      <input
        value={filterObject.name}
        onChange={(e) => {
          setFilterObject((prev) => ({ ...prev, name: e.target.value }));
        }}
        type="text"
        placeholder="Search..."
        style={{ margin: "20px", marginBottom: "0" }}
      />
      <br />
      <h6 style={{ color: "red", fontSize: "10px" }}>
        *red indicate unavailable items
      </h6>
      <label>
        <input
          type="checkbox"
          checked={filterObject.isStockedOnly}
          onChange={togglefilter}
        />{" "}
        Only Show Products in stock
      </label>
      <p>Search: {filterObject.name}</p>
      <Filtered />
    </div>
  );
}

Edit peaceful-zeh-1l8cee

UPD: Clarifications

Basically the idea is to have 1 items array as a state variable that holds the data loaded.

Next - state variable filterObject, just to keep everything search-related in 1 object.

Next - the filteredItems that are returned by useMemo hook which runs the function passed to it when anything in the depsArray, [items, filterObject] in our case is changed. It just watch for changes of items and filterObject and filters items based on what is in filterObject and returns array that match all the criterias.

Next - 2 useMemo hooks for fruits and vegetables, they just take the filtered array and apply 1 more filter by category, now you have 1 full array items with everything, 1 filteredItems, and 2 separate collections of fruits and vegetables, which are extracted from filteredItems.

Now just render them, and bind your inputs to change filterObject state.

Related