N
N
nivaech2019-05-21 09:41:49
JavaScript
nivaech, 2019-05-21 09:41:49

Why does the filter work without a bunch of other filters?

I have a React component with multiple content filters.
--- First - text search:

<div className="search-bar filter">
                <input type="text"
                       name="search" 
                       id='search' 
                       onChange={handleChange} 
                       value={search} 
                       placeholder = "Enter a title"
                       className="filter-item"/>
              </div>

The second is a search by color. There is a set with colors that takes them from the database of items, and maps these colors into tags:
<select name="color" 
                        id="color"
                        onChange={handleChange} 
                        value={color} 
                        className="filter-item select">

                    {
                    colors.map((color, index) => {
                      return (
                        <option key={index} value={color}> {color} </option>
                      )
                    })
                  }
                </select>

The third is search by price. The minimum value of goods is set, and the maximum. The slider selects the desired amount, and the component renders the number of products that corresponds to <= the given price.
<input 
                      type="range" 
                      name="price" 
                      id="price" 
                      min={min} 
                      max={max} 
                      value={price} 
                      className="range-price"
                      onChange = {handleChange}
 />

These filters are passed the necessary data from the state.
state = {
 search: '',
        price: 0,
        min: 0,
        max: 0,
        color: 'all',
        cup: 'all', 
}

It works on this principle. As soon as there is a change in the filter, the handleChange function is triggered, and then activates another function that already filters all requests, changes the state value of the element array, and renders the number of elements that corresponds to the parameters in the filters.
handleChange = (event) => {
    const name = event.target.name;
    const value = event.target.type === "checkbox" 
        ? event.target.checked 
        : event.target.value;
    this.setState({
        [name]: value
    },
    this.sortData
    );
};

sortData = () => {
    const {storeProducts, price, color, cup, shipping, search} = this.state;
    let tempProducts = [...storeProducts];
    let tempPrice = parseInt(price);

    // ---------- Filter by price
    tempProducts = tempProducts.filter(item => item.price <= tempPrice);

    // ---------- Filter by colors
    if (color !== "all") {
        tempProducts = tempProducts.filter(item => item.color === color)
    }
    
    // ---------- Filter by checkbox
    if(shipping) {
        tempProducts = tempProducts.filter(item => item.freeShipping === true)
    }
    if(search.length > 0) {
        tempProducts = tempProducts.filter(item => {
            let tempSearch = search.toLowerCase();
            let tempTitle = item.title.toLowerCase().slice(0, search.length);
            if (tempSearch === tempTitle) {
                 return item;
            }
        });
    }
    this.setState ({
        filteredProducts: tempProducts
    })
}

All these filters can be combined with each other. First, select a color, then you can set a price among the elements of the selected color - everything will be displayed correctly. But then I added another filter, in size, but for some reason, together with others, it does not work correctly, because it only works in tandem with a text filter. If you set the filter by size, and then either the price or the color - nothing. If you first select, for example, a color, and then a size, the color settings are lost, and only the size is displayed, that is, it interrupts all previous filters.
This filter looks like this:
<select name="cup" 
                        id="cup"
                        onChange={handleChange} 
                        className="filter-item select">
                  <option value="all" >all</option>
                  <option value="A" >A</option>
                  <option value="B" >B</option>
                  <option value="C" >C</option>
                  <option value="D" >D</option>
                </select>

And then I added a condition for it to the sortData function:
if (cup === "A") {
        tempProducts = brasAll.filter(item => item.cup.includes("A"))
    } else if (cup === "B") {
        tempProducts = brasAll.filter(item => item.cup.includes("B"))
    } else if (cup === "C") {
        tempProducts = brasAll.filter(item => item.cup.includes("C"))
    } else if (cup === "D") {
        tempProducts = brasAll.filter(item => item.cup.includes("D"))
    }

The final function looks like this:
sortData = () => {
    const {storeProducts, price, color, cup, shipping, search} = this.state;
    let tempProducts = [...storeProducts];
    let tempPrice = parseInt(price);
    let brasAll = storeProducts.filter(item => item.type === "Bras");
    // ---------- Filter by price
    tempProducts = tempProducts.filter(item => item.price <= tempPrice);
    // ---------- Filter by colors
    if (color !== "all") {
        tempProducts = tempProducts.filter(item => item.color === color)
    }
    // ---------- Filter by Cup
    if (cup === "A") {
        tempProducts = brasAll.filter(item => item.cup.includes("A"))
    } else if (cup === "B") {
        tempProducts = brasAll.filter(item => item.cup.includes("B"))
    } else if (cup === "C") {
        tempProducts = brasAll.filter(item => item.cup.includes("C"))
    } else if (cup === "D") {
        tempProducts = brasAll.filter(item => item.cup.includes("D"))
    }
    // ---------- Filter by checkbox
    if(shipping) {
        tempProducts = tempProducts.filter(item => item.freeShipping === true)
    }
    if(search.length > 0) {
        tempProducts = tempProducts.filter(item => {
            let tempSearch = search.toLowerCase();
            let tempTitle = item.title.toLowerCase().slice(0, search.length);
            if (tempSearch === tempTitle) {
                 return item;
            }
        });
    }
    this.setState ({
        filteredProducts: tempProducts
    })
}

Maybe someone has an idea why the new filter does not work in conjunction with the others and interrupts their values?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Spirin, 2019-05-21
@nivaech

Because you are filtering the original brasAll array, not the results. Basically, this code:

if (cup === "A") {
  tempProducts = brasAll.filter(item => item.cup.includes("A"))
} else if (cup === "B") {
  tempProducts = brasAll.filter(item => item.cup.includes("B"))
} else if (cup === "C") {
  tempProducts = brasAll.filter(item => item.cup.includes("C"))
} else if (cup === "D") {
  tempProducts = brasAll.filter(item => item.cup.includes("D"))
}

Can be replaced with:
if (cup) {
  tempProducts = tempProducts.filter(item => item.cup.includes(cup));
}

I see no real use for this canvas of code. In a good way in real applications they write like this:
handleSearch = () => {
  const { price, color, cup, shipping, search } = this.state;
  this.props.dispatch(fetchProducts({ price, color, cup, shipping, search });
};

Where fetchProducts is an action that initiates a request to the view server:
GET 'https://api.mysite.com/products?search=soes&price=120&color=red'

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question