Сортировка товаров на сайте с помощью тега
Всем привет, я новичок в React и только начинаю понимать азы. Сейчас я создаю сайт магазина одежды для самообучения, имитируя существующий сайт "spbtusa.ru"
На данный момент у меня проблема с сортировкой товаров на главной странице. Согласно идее, при выборе типа сортировки пользователь воздействует на атрибут onChange тега <select>
, этим действием изменяет State при помощи функции именения state - setCurrentSorting, добавляя в него функцию сортировки. При изменении State, список товаров, отображаемых на странице, должен быть отрисован заново.
Тег <select>
и вызываемая функция изменения состояния - setCurrentSorting, которые я передал в качестве props из родительского компонента (MainContentHeader.jsx)
<select onChange={(e) => {
switch (e.target.value) {
case "price:asc":
setCurrentSorting(SortPriceAsc);
break;
case "price:desc":
setCurrentSorting(SortPriceDesc);
break;
case "title:asc":
setCurrentSorting(SortTitleAsc);
break;
case "title:desc":
setCurrentSorting(SortTitleDesc);
break;
case "created:asc":
setCurrentSorting(SortCreatedAsc);
break;
case "created:desc":
setCurrentSorting(SortCreatedDesc);
break;
}
}} className="main-content-header__order">
<option data-filter-value="" value="">Порядок: по умолчанию</option>
<option data-filter-value="price:asc" value="price:asc">Цена: по возрастанию</option>
...
</select>
Простые функции сортировок (MainContentHeader.jsx)
function SortPriceDesc(a, b) {
if (
parseInt(a.price.replace(/\s/g, "")) > parseInt(b.price.replace(/\s/g, ""))
)
return -1;
else return 1;
}
function SortPriceAsc(a, b) {
if (
parseInt(a.price.replace(/\s/g, "")) < parseInt(b.price.replace(/\s/g, ""))
)
return -1;
else return 1;
}
...
Родительский компонент, связывающий список продуктов и <select>
для выбора сортировки (HomePage.jsx)
export default function HomePage() {
const [currentSorting, setCurrentSorting] = useState();
return (
<>
<IntroSlider />
<main className="main-content">
<MainContentHeader setCurrentSorting={()=>setCurrentSorting} />
<ProductSection currentTypeOfSorting={currentSorting} />
</main>
</>
);
}
Дочерний компонент, отображающий список продуктов. Функция loopWithSlice определяет, сколько продуктов мне нужно отобразить первыми из списка. (ProductSection.jsx)
const initialCountProducts = 36;
const productsPerСlick = 12;
export default function ProductSection({ currentFuncOfSorting }) {
const [limitedProductList, setProductsToShow] = useState([]);
const [next, setNext] = useState(initialCountProducts);
const [sortProductList, setSortProductList] = useState(productList);
useEffect(() => {
let sorted = [...sortProductList.sort(currentFuncOfSorting)];
setSortProductList(sorted);
setProductsToShow(sorted.slice(0, initialCountProducts));
}, [currentFuncOfSorting]);
const loopWithSlice = (start, end) => {
const slicedProduct = sortProductList.slice(start, end);
setProductsToShow(slicedProduct);
};
const handleShowMorePosts = () => {
loopWithSlice(next, next + productsPerСlick);
setNext(next + productsPerСlick);
};
return (
<>
<section className="product-section">
{limitedProductList.map((item, index) => (
<ProductCard key={index} {...item} />
))}
</section>
{limitedProductList.length < productList.length && (
<div className="product-section__button">
<button onClick={handleShowMorePosts}>Загрузить ещё</button>
</div>
)}
</>
);
}
Проблема в том, что мне нужно вызвать функцию loopWithSlice один раз во время первого рендеринга (как я понял, используя хук useEffect), и каждый раз, когда изменяется состояние родительского элемента (тип сортировки), должен изменяться sortProductList и ProductSection компонент должен быть отрисован повторно. Однако код либо переходит в бесконечный цикл, либо ничего не рендерит повторно..
Я буду рад, если вы, как более опытные, сможете сказать мне, что я делаю не так и почему это не работает. Повторяю, я новичок и еще многого не понял. Заранее спасибо.
Ответы (2 шт):
Проблема решена. Надеюсь, я правильно понимаю, в state можно добавлять только функции, которые не принимают аргументы, поэтому мы превращаем функцию сортировки в функцию со стрелкой.
Если я ошибаюсь, поправьте меня, пожалуйста.
<select
onChange={(e) => {
switch (e.target.value) {
case "price:asc":
setSorting(() => SortPriceAsc);
break;
case "price:desc":
setSorting(() => SortPriceDesc);
break;
...
default:
setSorting(() => DefaultSortIdAsc);
}
}}
className="main-content-header__order"
>
<option data-filter-value="" value="">
Порядок: по умолчанию
</option>
<option data-filter-value="price:asc" value="price:asc">
Цена: по возрастанию
</option>
<option data-filter-value="price:desc" value="price:desc">
Цена: по убыванию
</option>
...
</select>
const [a, setA] = useState()
setA
имеет два синтексиса:
- setA(значение)
- setA((предидушее значение) => значение)
когда ты передал в сеттер функцию он подумал что это втарой синтексис и вызвал ее от сюда и проблемы
вот алтернативное решение
const getIntegerPrice = (product) => parseInt(product.price.replace(/\s/g, ""));
const sortPriceDesc = (a, b) => {
const firstPrice = getIntegerPrice(a);
const secondPrice = getIntegerPrice(b);
if (firstPrice > secondPrice) {
return -1;
}
return 1;
};
const sortPriceAsc = (a, b) => {
const firstPrice = getIntegerPrice(a);
const secondPrice = getIntegerPrice(b);
if (firstPrice < secondPrice) {
return -1;
}
return 1;
};
const SORTING = {
"price:asc": {
key: "price:asc",
sortingFn: sortPriceAsc,
},
"price:desc": {
key: "price:desc",
sortingFn: sortPriceDesc,
},
};
const HomePage = () => {
const [currentSortingType, setCurrentSortingType] = useState();
//можно дастать и тут и в ProductSection прабросив туда currentSortingType
//если пробросить currentSortingType то она и станет зависимостю для useEffect
//если нет то зависимостю будет currentSortingFn
const currentSortingFn = SORTING[currentSortingType].sortingFn;
return (
<>
<main>
<MainContentHeader setCurrentSortingType={setCurrentSortingType} />
{/* <ProductSection currentSortingFn={currentSortingFn} /> */}
{/* либо */}
{/* <ProductSection currentSortingType={currentSortingType} /> */}
</main>
</>
);
};
const MainContentHeader = ({ setCurrentSortingType }) => {
return (
<select
onChange={({ target: { value } }) => {
if (value) {
setCurrentSortingType(value);
}
}}
>
<option value={SORTING["price:asc"].key}>x</option>
<option value={SORTING["price:desc"].key}>y</option>
</select>
);
};