Ali Ziya ÇEVİK
Ali Ziya ÇEVİK

Reputation: 198

How can i filter my State with onChange in reactJs?

I want to filter my products with onChange event. For example, If I just search for 'a' I want to see every product that includes 'a' If I delete 'a' or whatever I just searched, I want to see all my products back without refresing the page.

Here is my code below; all it does filter what i search and If I ever delete what I type It doesn't' give me back all the products.

I know I'm mutating my products with the solution I made but I want to know how to achieve this problem.

import React, { useEffect, useState } from "react";
import { getProductKinds, getProducts } from "../lookup";

function Products() {

  //const [count, setCount] = useState(0);
  const [products, setProducts] = useState([]);
  useEffect(() => {

    const myCallBack = (response, status) => {
      if (status === 200) {
        console.log("products resnpose:", response);
        setProducts(response);
      }
    };
    
    getProducts(myCallBack);
  },[] );
  const filterProducts = (event) => {
    let filterText = event.target.value;
    let tempProducts = [...products];
    setProducts(tempProducts.filter(product => {
      console.log(product.name.includes(filterText));
      if (product.name.includes(filterText)) return product;
    }))
    
  }

  return (
    <div className="products-page">
      <div className="card-filters">
        <form>
          <div class="form-search col mt-5 ml-2 ">
            <input
              onChange = {filterProducts}
              type="text"
              name="text"
              class="form-control"
              placeholder="Search"
            />
          </div>

          <p></p>
          <h5 clas>Category</h5>
          <hr></hr>
          <div className="form-buttons container mt-4">
            <button className="btn btn-outline-secondary btn-sm">All</button>
            <p></p>
            <button className="btn btn-outline-secondary btn-sm">
              Electronic
            </button>
            <p></p>
            <button className="btn btn-outline-secondary btn-sm">
              Furniture
            </button>
            <p></p>
            <button className="btn btn-outline-secondary btn-sm">Dining</button>
            <hr></hr>
          </div>
          <div>
           <div className ="form-price">
           <h5>Price</h5>
           <p > 2790.99$</p>
           <input type="range" name="price" min="0" max="309999" value="309999">
              </input>
              </div> 

          </div>
          <hr></hr>
        </form>
      </div>
      <div className="card-group container">
        {products.map((product) => {
          return (
            <div>
              <div className="card shadow" style={{ width: "18rem" }}>
                <img
                  className="card-img-top"
                  src="https://dl.airtable.com/.attachmentThumbnails/65708b701baa3a84883ad48301624b44/2de058af"
                  alt="Card image cap"
                />
                <a class="link" href="/products/recroK1VD8qVdMP5H">
                  <svg
                    stroke="currentColor"
                    fill="white"
                    stroke-width="0"
                    viewBox="0 0 512 512"
                    height="1em"
                    width="1em"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path>
                  </svg>
                </a>
                <div className="card-footer">
                  <p className="card-text">{product.name}</p>
                  <p className="card-price" style={{ float: "right" }}>
                    {product.price}$
                  </p>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}
export default Products;


Upvotes: 0

Views: 1323

Answers (3)

ljbc1994
ljbc1994

Reputation: 2254

Inside filterProducts you're overwriting the state with the filtered state, so when you remove the search query it's trying to search on items that no longer exist in the state.

To overcome this, you can use React.useMemo to perform the filtering whilst leaving alone the state you retrieved from the server...

const [products, setProducts] = useState([]);
const [filterText, setFilter] = useState("");

const filterProducts = (event) => {
    setFilter(event.target.value);
}

const filtered = React.useMemo(() => {
    return products.filter(product => {
      return filterText.length > 0 ? product.name.includes(filterText) : true;
    })
}, [filterText, products]);

// Use the filtered variable
return (
    {filtered.map((product) => {
);

Upvotes: 2

technicallynick
technicallynick

Reputation: 1592

It's not resetting to the default because you're constantly changing the source list. Try doing something like this:

const [filteredProducts, setFilteredProducts] = useState(null)
useEffect(() => {
  setFilteredProducts(products)
}, [products])
const filterProducts = (event) => {
    
    let filterText = event.target.value;
    let tempProducts = [...products];
    setFilteredProducts(tempProducts.filter(product => {
      console.log(product.name.includes(filterText));
      if (product.name.includes(filterText)) return product;
    }))
    
  }

And then use filteredProducts for your input.

Upvotes: 0

VersifiXion
VersifiXion

Reputation: 2292

I would put my response data in 2 arrays, setAllProducts(response); and setProducts(response);,

If your input is empty, you show all products by doing setProducts(allProducts), if it has 1 character and more you show the products by doing setProducts(YOUR_FILTER_HERE)

I would do something like that using 2 arrays, maybe :

const myCallBack = (response, status) => {
      if (status === 200) {
        console.log("products resnpose:", response);
        setAllProducts(response);
        setProducts(response);
      }
};

const filterProducts = (event) => {
    let filterText = event.target.value;
    if (event.target.value.length === 0) {
      setProducts(allProducts);
    } else {
      setProducts(products.filter(product => {
        console.log(product.name.includes(filterText));
        if (product.name.includes(filterText)) return product;
      }))
    }
}

Upvotes: 1

Related Questions