@Nivaech

Почему фильтр работает без связки с другими фильтрами?

Есть компонент в React с несколькими фильтрами контента.

--- Первый - текстовый поиск:
<div className="search-bar filter">
                <input type="text"
                       name="search" 
                       id='search' 
                       onChange={handleChange} 
                       value={search} 
                       placeholder = "Enter a title"
                       className="filter-item"/>
              </div>


Второй - поиск по цвету. Есть сет с цветами, который берет их из базы данных предметов, и мапит эти цвета в теги :
<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>


Третий - поиск по цене. Задано минимальное значение товаров, и максимальное. Ползунком выбирается нужная сумма, и компонент рендерит количество товаров, которое соответствует <= заданной цены.
<input 
                      type="range" 
                      name="price" 
                      id="price" 
                      min={min} 
                      max={max} 
                      value={price} 
                      className="range-price"
                      onChange = {handleChange}
 />


В эти фильтры передаются необходимые данные из state.
state = {
 search: '',
        price: 0,
        min: 0,
        max: 0,
        color: 'all',
        cup: 'all', 
}

Работает по такому принципу. Как только в фильтре происходит изменение, срабатывает функция handleChange, и потом активирует другую функцию, которая уже фильтрует все запросы, изменяет значение state массива элементов, и рендерит то количество элементов, которое соответствует параметрам в фильтрах.

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
    })
}


Все эти фильтры между собой можно совмещать. Сначала выбрать цвет, потом среди элементов выбранного цвета можно установить цену - все будет отображаться корректно. Но потом я добавил еще один фильтр, по размеру, но почему-то совместно с другими он работает неправильно, потому как работает в паре только с текстовым фильтром. Если задать фильтр по размеру, а потом или цену, или цвет - ничего. Если сначала выбрать, например цвет, а потом размер - настройки цвета сбиваются, и выдается лишь размер, то есть он перебивает все прошлые фильтры.
Фильтр этот выглядит так:
<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>


И после дописал условие для него в функцию sortData:
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"))
    }


Финальная функция выглядит так:
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
    })
}


Может будуть у кого-то идеи, почему новый фильтр работает не в связке с остальными и перебивает их значения?
  • Вопрос задан
  • 113 просмотров
Решения вопроса 2
rockon404
@rockon404 Куратор тега React
Frontend Developer
Потому-что фильтруете исходный массив brasAll, а не результаты. Вообще, этот код:
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"))
}

Можно заменить на:
if (cup) {
  tempProducts = tempProducts.filter(item => item.cup.includes(cup));
}

Я не вижу реального применения этому полотну кода. По-хорошему в реальных приложениях пишут так:
handleSearch = () => {
  const { price, color, cup, shipping, search } = this.state;
  this.props.dispatch(fetchProducts({ price, color, cup, shipping, search });
};

Где fetchProducts это экшен иницирующий запрос на сервер вида:
GET 'https://api.mysite.com/products?search=soes&price=120&color=red'
Ответ написан
0xD34F
@0xD34F Куратор тега React
А почему вы фильтруете разные массивы? - в одном случае brasAll (это как раз тот, который "перебивает все прошлые фильтры"), а в остальных tempProducts.

Почему метод,ответственный за фильтрацию, имеет в своём имени слово "sort"?

Ну и длинно как-то всё, не помешало бы сократить, можно сделать массив фильтров, элементы которого будут состоять из двух значений - применяем/нет фильтр и собственно функция фильтрации, затем прогоняем через полученные фильтры массив с данными, как-то так:

const filteredProducts = [
  [            true, n => n.type === 'Bras' ],
  [            true, n => n.price <= tempPrice ],
  [ color !== 'all', n => n.color === color ],
  [             cup, n => n.cup.includes(cup) ],
  [        shipping, n => n.freeShipping === true ],
  [          search, n => n.title.toLowerCase().startsWith(search.toLowerCase()) ],
].reduce((products, [ active, fn ]) => {
  return active
    ? products.filter(fn)
    : products;
}, storeProducts);

this.setState({ filteredProducts });
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы