Фильтровать и сортировать вместе в реакции

Я не могу фильтровать и сортировать одновременно. Я буду получать products от axios get call и сначала создавать его копию в filteredProducts. Затем я буду нажимать на параметры фильтра sort и discount. Здесь сортировка работает нормально, но не вместе с фильтром discount. Как я могу достичь этого вместе внутри useEffect?

    const [filteredProducts, setFilteredProducts] = useState([]);

    const handleSort = (sortByValue) => {
        return filteredProducts.sort((a, b) => {
            switch(sortByValue) {
                case 'new' : return parseISO(b.created_at) - parseISO(a.created_at);
                case 'discount' : return b.discount - a.discount;
                case 'price_desc' : return b.price - a.price;
                case 'price_asc' : return a.price - b.price;
                default : return a.name.localeCompare(b.name);
            }
        })
    }
    
    const discount = queryParams.get('discount')
    const sortBy = queryParams.get('sort')

    const handleDiscountFilters = (filterResults) => {
        if (discount) {
            return filteredProducts.filter((product) => product.discount > discount)
        } else {
            return products
        }
    }
    
    // Here i want to call the different filter functions first and then call sort function. 
    useEffect(() => {
        let filterResults = [...filteredProducts];
        filterResults = handleDiscountFilters(filterResults)
        filterResults = handleSort(sortBy)
        setFilteredProducts(filterResults);
    }, [filteredProducts, handleDiscountFilters, handleSort, sortBy])
sort и filter — это две разные вещи, которые не следует путать. Сначала filter данные, а потом sort они. Но если вы sort сделаете это сначала, а потом filter вам придется sort снова, так как не гарантируется, что отфильтрованные данные будут отсортированы.
Mohammed Shahed 22.05.2023 11:08
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
62
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы должны передать массив функции handleSort. В противном случае вы сортируете исходный массив, а не отфильтрованный

const handleSort = (products, sortByValue) => {
  return products.sort((a, b) => {
    switch (sortByValue) {
      case "new":
        return parseISO(b.created_at) - parseISO(a.created_at);
      case "discount":
        return b.discount - a.discount;
      case "price_desc":
        return b.price - a.price;
      case "price_asc":
        return a.price - b.price;
      default:
        return a.name.localeCompare(b.name);
    }
  });
};
useEffect(() => {
  let filterResults = [...filteredProducts];
  filterResults = handleDiscountFilters(filterResults);
  filterResults = handleSort(filterProducts, sortBy);
  setFilteredProducts(filterResults);
}, [filteredProducts, handleDiscountFilters, handleSort, sortBy]);
Ответ принят как подходящий

Есть несколько проблем с вашим кодом. Во-первых, вы изменяете и устанавливаете filterResults в одном и том же хуке useEffect, что означает, что у вас будет бесконечный цикл. Каждый раз, когда вы вызываете setFiltereddProducts, эталонное значение filteredProducts изменяется, что заставит ваш useEffect снова срабатывать.

Далее, аналогично тому, как вы передаете filterResults в handleDiscountFilters, вам также нужно сделать это для handleSort. Прямо сейчас handleSort читает с верхнего уровня filteredProducts, на который не влияют ваши фильтры:

  let filterResults = [...filteredProducts];
  filterResults = handleDiscountFilters(filterResults)
  filterResults = handleSort(filterResults, sortBy)

Но, кроме сетевого звонка, для этого вообще не нужен useEffect:

const [products, setProducts] = useState([]);

useEffect(async () => {
    const response = await axios.get("someUrl");
    setProducts(response.data);
}, []);

const discount = queryParams.get('discount');
const sortBy = queryParams.get('sort');

const shouldFilterProduct = (product) => {
    if (discount) {
        return product.discount > discount;
    } else {
        return false;
    }
};

const sortProduct = (a, b) => {
    switch(sortByValue) {
        case 'new' : return parseISO(b.created_at) - parseISO(a.created_at);
        case 'discount' : return b.discount - a.discount;
        case 'price_desc' : return b.price - a.price;
        case 'price_asc' : return a.price - b.price;
        default : return a.name.localeCompare(b.name);
    }
}

const filteredAndSortedProducts = products
    .filter(filterProduct)
    .sort(sortProduct);

return (
    <div>
        {filteredAndSortedProducts.map(product => {
            return <div>{product.name}</div>;
        })}
    </div>
);

Этот краткий код был именно тем, что я искал. Это сработало!

Srivatsa 22.05.2023 12:30

Проблема в том, что handleSort использует значение, объявленное в первой строке кода, поэтому значение filterResults, присвоенное из возвращаемого значения handleDiscountFilters, теряется, вы всегда используете нефильтрованные данные.

Итак, чтобы исправить это, я бы добавил параметр в функцию handleSort и использовал этот параметр вместо константы, объявленной в первой строке:

    const [filteredProducts, setFilteredProducts] = useState([]);

    const handleSort = (values, sortByValue) => {
        return values.sort((a, b) => {
            switch(sortByValue) {
                case 'new' : return parseISO(b.created_at) - parseISO(a.created_at);
                case 'discount' : return b.discount - a.discount;
                case 'price_desc' : return b.price - a.price;
                case 'price_asc' : return a.price - b.price;
                default : return a.name.localeCompare(b.name);
            }
        })
    }
    
    const discount = queryParams.get('discount')
    const sortBy = queryParams.get('sort')

    const handleDiscountFilters = (filterResults) => {
        if (discount) {
            return filteredProducts.filter((product) => product.discount > discount)
        } else {
            return products
        }
    }
    
    // Here i want to call the different filter functions first and then call sort function. 
    useEffect(() => {
        let filterResults = [...filteredProducts];
        filterResults = handleDiscountFilters(filterResults)
        filterResults = handleSort(filterResults, sortBy)
        setFilteredProducts(filterResults);
    }, [filteredProducts, handleDiscountFilters, handleSort, sortBy])

Вот почему фильтрация игнорируется, но есть и другие проблемы, я думаю: разве это не отображается непрерывно в цикле? Поскольку функции handle___ являются новыми при каждом рендеринге, поэтому useEffect будет вызываться также при каждом рендеринге, а useEffect всегда вызывает setFilteredProducts, поэтому он запускает другой рендеринг, поэтому он никогда не останавливает рендеринг. Кроме того, если вы установите значение после первого рендеринга, отфильтрованные данные останутся отфильтрованными (отфильтрованные значения будут потеряны). У меня была бы переменная, связанная с состоянием, для исходных нефильтрованных данных и другая, полученная из нее, для отфильтрованных данных.

Другие вопросы по теме