Я пытаюсь сортировать продукты по цене: низкая-высокая или высокая-низкая, что я делал раньше в vanilla JS, но на этот раз в React. У меня есть раскрывающийся список, в котором можно выбрать желаемый порядок, и сама функция работает, но не так, как мне нужно.
В моей локальной версии кода, если вы выбираете порядок сортировки из раскрывающегося списка, в этот момент он ничего не делает, однако, если вы затем фильтруете продукты, скажем, по бренду или категории, он применяет как фильтр бренда (или категории), так и выбранный фильтр порядка сортировки цен. Затем вы можете легко изменить порядок сортировки с низкого-высокого на высокий-низкий или наоборот.
Таким образом, кажется, что выбранный порядок сортировки применяется правильно, но он просто не отображает изменение useState во время выбора в раскрывающемся списке, и я не знаю, почему.
Вкратце, вот биты, управляющие функциональностью порядка сортировки:
const [filterData, setFilterData] = useState(productDataList.products || []);
const sortItems = (e) => {
console.info('Sort');
const sortedProducts = filterData.sort((a, b) => {
if (e.value == "LowToHigh") {
console.info('sorted low to high');
if (a.price < b.price) {
return -1;
} else {
return 1;
}
} else if (e.value == "HighToLow") {
console.info('sorted high to low');
if (a.price > b.price) {
return -1;
} else {
return 1;
}
}
});
setFilterData(sortedProducts);
};
return (
<select id = "sortDropDown" onChange = {(e) => sortItems(e.target)}>
<option value = "default">Sort by:</option>
<option value = "LowToHigh">Low to high</option>
<option value = "HighToLow">High to low</option>
</select>
)
Надеюсь, этого будет достаточно, чтобы кто-то помог предоставить информацию об исправлении, поскольку я не понимаю, почему оно не обновляется при выборе варианта из раскрывающегося списка.
Если нет, я добавил больше кода ниже для страницы (хотя это слегка урезанная версия и обычно разбивается на несколько компонентов, и я попытался объединить его ниже, но если что-то еще необходимо, чтобы иметь возможность помочь пожалуйста, дайте мне знать.
Заранее спасибо.
const {useState, useEffect} = React
const productDataList = {
"products": [
{
"id": 1,
"title": "Essence Mascara Lash Princess",
"category": "beauty",
"price": 9.99,
"brand": "Essence",
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Essence%20Mascara%20Lash%20Princess/thumbnail.png"
},
{
"id": 2,
"title": "Eyeshadow Palette with Mirror",
"category": "beauty",
"price": 19.99,
"brand": "Glamour Beauty",
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Eyeshadow%20Palette%20with%20Mirror/thumbnail.png"
},
{
"id": 3,
"title": "Powder Canister",
"category": "beauty",
"price": 14.99,
"brand": "Velvet Touch",
"thumbnail": "https://cdn.dummyjson.com/products/images/beauty/Powder%20Canister/thumbnail.png"
},
{
"id": 4,
"title": "Dior J'adore",
"category": "fragrances",
"price": 89.99,
"brand": "Dior",
"thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dior%20J'adore/thumbnail.png"
},
{
"id": 5,
"title": "Dolce Shine Eau de",
"category": "fragrances",
"price": 69.99,
"brand": "Dolce & Gabbana",
"thumbnail": "https://cdn.dummyjson.com/products/images/fragrances/Dolce%20Shine%20Eau%20de/thumbnail.png"
},
{
"id": 6,
"title": "Annibale Colombo Sofa",
"category": "furniture",
"price": 2499.99,
"brand": "Annibale Colombo",
"thumbnail": "https://cdn.dummyjson.com/products/images/furniture/Annibale%20Colombo%20Sofa/thumbnail.png"
},
],
"total": 194,
"skip": 0,
"limit": 36
}
const App = () => {
const [productsLimit, setProductsLimit] = useState(productDataList.limit)
const [filterData, setFilterData] = useState(productDataList.products || []);
const [categories, setCategories] = useState([]);
const [brands, setBrands] = useState([]);
useEffect(() => {
let filteredCategories = [],
filteredBrands = [];
productDataList.products.forEach((product) => {
if (!filteredCategories.includes(product.category)) {
filteredCategories.push(product.category);
}
if (!filteredCategories.includes(product.brand)) {
filteredBrands.indexOf(product.brand) == -1 && product.brand != '' && product.brand != undefined && product.brand != null ? filteredBrands.push(product.brand) : null;
}
});
setCategories(filteredCategories);
setBrands(filteredBrands);
}, []);
const productFilters = (products, filt) => {
let filteredProducts = [];
if (categories.includes(filt)) {
filteredProducts = products.category.toLowerCase() === filt.toLowerCase();
} else if (brands.includes(filt)) {
filteredProducts = products.brand === filt;
}
return filteredProducts;
};
const filter = (byFilter) => {
if (byFilter) {
let productsData = [...productDataList.products];
productsData = productsData.filter((filter) => {
return productFilters(filter, byFilter);
});
setFilterData(productsData);
} else {
setFilterData(productDataList.products);
}
};
const sortItems = (e) => {
console.info('Sort');
const sortedProducts = filterData.sort((a, b) => {
if (e.value == "LowToHigh") {
console.info('sorted low to high');
if (a.price < b.price) {
return -1;
} else {
return 1;
}
} else if (e.value == "HighToLow") {
console.info('sorted high to low');
if (a.price > b.price) {
return -1;
} else {
return 1;
}
}
});
setFilterData(sortedProducts);
};
return (
<div>
<select id = "sortDropDown" onChange = {(e) => sortItems(e.target)}>
<option value = "default">Sort by:</option>
<option value = "LowToHigh">Low to high</option>
<option value = "HighToLow">High to low</option>
</select>
<select onChange = {(e) => filter(e.target.value)}>
<option value = "Filter Categories" selected = "selected">Filter by categories</option>
{categories.map((catFilter) => (
<option value = {catFilter}>{catFilter}</option>
))}
</select>
<select onChange = {(e) => filter(e.target.value)}>
<option value = "Filter Categories" selected = "selected">Filter by brands</option>
{brands.map((item) => (
<option value = {item}>{item}</option>
))}
</select>
<button onClick = {filter}>Clear filter</button>
<div className='products'>
{filterData?.slice(0, productsLimit).map(product => (
<div className='product' key = {product.id}>
<div className='productImage'>
<img src = {product.thumbnail} />
</div>
<div className='productInfo'>
<div className='productTitle'>
<div className='productBrand'>{product.brand}</div>
<div className='productName'>{product.title}</div>
<div className='productName'>{product.title}</div>
</div>
<div className='productPrice'>Price: £{product.price}</div>
{}
</div>
</div>
))}
</div>
</div>
)
}
ReactDOM.render(<App />, document.querySelector('#root'));
.products {
display: flex;
flex-wrap: wrap;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src = "https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id = "root"></div>
🤔 А знаете ли вы, что...
С помощью JavaScript можно валидировать данные на стороне клиента, что улучшает пользовательский опыт.
Ваше понимание React неверно. пожалуйста, укажите правильный подход ниже.
// const [filterData, setFilterData] = useState(productDataList.products || []);
let [orderType,setOrderType] = useState("default")
let filterData = useMemo(()=>{
// write your order code with orderType ...
},[orderType])
return (
<select onChange = {(e) => setOrderType(e.target)}>
<option value = "default">Sort by:</option>
<option value = "LowToHigh">Low to high</option>
<option value = "HighToLow">High to low</option>
</select>
)
Спасибо @Robin Zigmond за его комментарий, просто для более быстрого получения ответа я добавляю его сюда.
Изменение .sort
на .toSorted
мгновенно решило проблему.
const sortItems = (e) => {
console.info('Sort');
const sortedProducts = filterData.toSorted((a, b) => {
if (e.value == "LowToHigh") {
console.info('sorted low to high');
if (a.price < b.price) {
return -1;
} else {
return 1;
}
} else if (e.value == "HighToLow") {
console.info('sorted high to low');
if (a.price > b.price) {
return -1;
} else {
return 1;
}
}
});
setFilterData(sortedProducts);
};
Это объясняется тем, что:
sort
изменяет массив и возвращает ту же ссылку (см. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort ), поэтому React «не видит» «Любое изменение. Самое простое решение, если вам не нужна поддержка браузеров старше года или около того, — использовать новыйtoSorted
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Global_Objects/Array/toSorted] вместоsort
Я по-прежнему считаю, что решение/обходной путь, который будет работать со старыми браузерами, будет полезным, и я рад отметить это как ответ, если оно будет добавлено, но сейчас спасибо @Robin Zigmond за это решение.
Вы можете установить состояние в скопированном массиве с помощью:
const sortItems = (e) => {
console.info('Sort');
const priceFilterData = [...filterData]
priceFilterData.sort((a, b) => {
if (e.value == "LowToHigh") {
console.info('sorted low to high');
if (a.price < b.price) {
return -1;
} else {
return 1;
}
} else if (e.value == "HighToLow") {
console.info('sorted high to low');
if (a.price > b.price) {
return -1;
} else {
return 1;
}
}
});
setFilterData(sortedProducts);
};
Это позволит обойти изменение исходного массива, используемого для вашего useState, которое происходило раньше.