@Nivaech

Почему фильтр срабатывает только после второго клика?

Есть фильтр по цвету. По нажатию на определенный цвет его значение заносится в state, а потом с помощью фильтра массива должны отображатся соответствующие элементы. Но проблема в том, что фильтр срабатывает только после второго нажатия, а не сразу.
1. В компонент подгружается массив данных, и сохраняется в в filteredItems. Тут же значения state:
const Products = ({ collection, indiVisible, match }) => {
    const { items } = collection;

    useEffect(() => {
        let tempItems = [...items]
        setFilteredItems(tempItems)
    }, [items])

    // ---------------------- Defining states
    const [ filteredItems, setFilteredItems ] = useState(items);
    const [ colorFilter, setColorFIlter ] = useState('');
...
...

2. Ниже приведены функции, которые отвечают за изменения массива (отфильтрованных элементов) и функция фильтра по цвету.
// ---------------------- Sort items
    const sortItems = () => {
        let tempItems = [...items];
        // -----> Filter by colors
        if (colorFilter) {
            tempItems = tempItems.filter(item => item.color.includes(colorFilter));
        }
        setFilteredItems(tempItems);
    }

// ---------------------- Filter Item by color
    const filterByColor = (color) => {
        setColorFIlter(color);
        console.log(`now color is ${color}`)
        sortItems();
    };

3. В итоге мапится filteredItems и отображает предметы.
<div className="items">
                    {
                        filteredItems.map(item => (
                            <ProductCard 
                                key={item.id} 
                                item={item} 
                                defineSingle={defineSingle}
                            />
                        ))
                    }
</div>

4. Так выглядит компонент фильтра. Функция мапит цвета, заданные в бд, и отображает их в панели. На клик по цвету срабатывает функция filterByColor(color), которая заносит выбраные цвет в стейт, а потом вызывает функцию sortItems(), которая должна принять значение фильтра и отрендерить новый массив, уже отфильтрованный.
<ul className={`filter-list  ${colorFilter && "opened-filter"}`}>
            {   
                colors.map((color, index) => (  
                    <li className="filter-item color-filter" key={index} onClick={() => filterByColor(color)}> 
                        <div className="color-ball" style={{ backgroundColor: color }} />
                            {color} 
                        </li>
                 ))
            }
            <button className="btn" onClick={dropColorFIlter}>Clear</button>
</ul>


Все работает, но только после второго клика по значению цвета в фильтре. Что не так?

UPD: Кажется, что проблема в том, что на клик по цвету его значение отправляется в setColorFIlter не сразу по нажатию, и что проблема именно в нем. Но проблема пока не решена.
  • Вопрос задан
  • 93 просмотра
Решения вопроса 1
@Nivaech Автор вопроса
Проблема была решена.
1. Вместо передачи компоненту фильтров функции filterByColor() нужно было передать напрямую setColorFIlter с
useState.
2. Добавить новый эффект, реагирующий на изменение стейта и вызывающий функцию сортировки sortItems()
useEffect(() => {
        if (setColorFilter) {
            sortItems()
        }
    }, [colorFilter, sortItems])

3. Саму функцию sortItems() переписать с учетом хука useCallback:
const sortItems = useCallback(() => {
        let tempItems = [...items];
        // -----> Filter by colors
        if (colorFilter) {
            tempItems = tempItems.filter(item => item.color.includes(colorFilter));
        } 

        setFilteredItems(tempItems);
            console.log(`items are sorted. new array has ${tempItems.length} items`)
    }, [colorFilter, items])


И все работает
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы