reactjs - 使用 redux 单击时应用和重置多个过滤器
问题描述
我需要列出将在点击时过滤的产品。但我只是无法掌握如何在反应中做到这一点。主要是:
- 在哪里做过滤功能?
- 如何组合并将数据传递给reducer?
- 如何用 1 个按钮触发它。
我敢肯定,对于经验丰富的开发人员来说,这是非常微不足道的案例。
到目前为止我的代码存储库:https ://github.com/SolidMike/react-hotels/tree/main/src/
至于现在我有输出我的 json 的组件和包含所有过滤器的组件。遗憾的是,我只是不明白如何使它与我的动作和减速器一起工作:(
这是我的过滤器
const mapStateToProps = (state) => {
return {
filter: state.filter,
}
}
const mapDispatchToProps = { filtersApply, filtersReset }
function Filter({ filter, filtersApply, filtersReset }) {
console.log(filter)
const typesOptions = [
{ value: 'appartment', label: 'Апартаменты' },
{ value: 'hotel', label: 'Отель' },
]
const countriesOptions = [
{ value: 'greece', label: 'Греция' },
{ value: 'russian', label: 'Россия' },
{ value: 'ukraine', label: 'Украина' },
]
const [types, setTypes] = useState([])
const [countries, setCountries] = useState([])
const handleOnChangeCountries = (selectedOption) => {
setCountries(selectedOption)
console.log(`Option selected:`, selectedOption)
}
const handleOnChangeTypes = (selectedOption) => {
setTypes(selectedOption)
console.log(`Option selected:`, selectedOption)
}
return (
<div>
<div className="filter__country">
<Select
defaultValue={countries}
onChange={handleOnChangeCountries}
options={countriesOptions}
/>
</div>
<label for="type">
Тип
<Select
defaultValue={types}
options={typesOptions}
onChange={handleOnChangeTypes}
isMulti
/>
</label>
<fieldset>
<legend>Количество звезд</legend>
<label for="one_star">
1 звезда
<input
type="checkbox"
name="rating"
id="one_star"
value="1"
/>
</label>
<label for="two_stars">
2 звезды
<input
type="checkbox"
name="rating"
id="two_stars"
value="2"
/>
</label>
<label for="three_stars">
3 звезды
<input
type="checkbox"
name="rating"
id="three_stars"
value="3"
/>
</label>
<label for="four_stars">
4 звезды
<input
type="checkbox"
name="rating"
id="four_stars"
value="4"
/>
</label>
<label for="five_stars">
5 звезд
<input
type="checkbox"
name="rating"
id="five_stars"
value="5"
/>
</label>
</fieldset>
<button
className="filter__apply"
onClick={() => {
filtersApply({ name: 'countryFilter', value: countries })
}}
>
Применить фильтр
</button>
<button className="filter__reset" onClick={filtersReset}>
Очистить фильтр
</button>
</div>
)
}
export default connect(mapStateToProps, mapDispatchToProps)(Filter)
reducer.js 我是否应该将数组置于初始状态,我不确定。
let initialState = {
hotels: [
{
name: 'Marina Inn',
country: 'Греция',
address: 'Фалираки, Родос, Греция',
stars: 4,
type: 'Отель',
description:
'Этот 4-звездочный отель расположен в центре города. На его территории есть бассейн с террасой и сауна. Из номеров открывается вид на море или на средневековый город.',
services: [
'Пляж',
'Кондиционер',
'Открытый бассейн',
'Бесплатная парковка',
'Бесплатный WiFi',
'Вид на море',
'Бесплатный завтрак',
],
min_price: 2789.0,
currency: 'RUR',
rating: 4.5,
reviews_amount: 18,
last_review:
'Отличное расположение. Вкусный завтрак. Отдыхали с детьми - все понравилось.',
},
{
name: 'Mondrian Suites',
country: 'Греция',
address: 'Фалираки, Родос, Греция',
stars: 5,
type: 'Апартаменты',
description:
'Из окон открывается вид на город.К услугам гостей номера-студио с балконом и чайником в 2,7 км от пляжа.',
services: [
'Пляж',
'Кондиционер',
'Открытый бассейн',
'Платная парковка',
'Бесплатный WiFi',
'Вид на море',
],
min_price: 3245.2,
currency: 'RUR',
rating: 5,
reviews_amount: 4,
last_review:
'Потрясающее место, в номере есть все необходимое. Красивый вид на море.',
},
{
name: 'Sunny Appartments',
country: 'Греция',
address: 'Родос, Родос, Греция',
stars: 2,
type: 'Апартаменты',
description:
'Все номера и апартаменты оборудованы кондиционером и телевизором с плоским экраном. Также в распоряжении гостей тостер и чайник.',
services: [
'Пляж',
'Кондиционер',
'Бесплатная парковка',
'Бесплатный WiFi',
],
min_price: 1153.0,
currency: 'RUR',
rating: 2.4,
reviews_amount: 36,
last_review:
'Бассейн очень маленький. Кормят невкусно. Больше не поедем.',
},
{
name: 'Super All Inclusive Hotel',
country: 'Греция',
address: 'Родос, Родос, Греция',
stars: 4,
type: 'Отель',
description:
'Все номера оснащены телевизором с плоским экраном. Из некоторых номеров открывается вид на море или город.',
services: [
'Пляж',
'Кондиционер',
'Открытый бассейн',
'Бесплатный WiFi',
'Вид на море',
'Бесплатный завтрак',
],
min_price: 3689.0,
currency: 'RUR',
rating: 4.1,
reviews_amount: 14,
last_review:
'Недалёко от пляжа и старого города, вокруг много разных магазинчиков',
},
{
name: 'Adams Hotel',
country: 'Греция',
address: 'Родос, Родос, Греция',
stars: 3,
type: 'Отель',
description:
'Отель расположен всего в 100 метрах от пляжа и в 5-ти минутах ходьбы от исторической части города, недалеко от всех основных достопримечательностей. Из отеля открывается вид на море. К услугам гостей спокойный открытый бассейн.',
services: [
'Пляж',
'Кондиционер',
'Открытый бассейн',
'Бесплатная парковка',
'Бесплатный WiFi',
'Бесплатный завтрак',
],
min_price: 1896.0,
currency: 'RUR',
rating: 0,
reviews_amount: 0,
last_review: '',
},
],
countryFilter: [],
typeFilter: [],
starsFilter: [],
reviewsAmountFilter: null,
}
export const FiltersReducer = (state = initialState, action) => {
switch (action.type) {
case 'FILTER_UPDATE': {
console.log(action.payload)
const { name, value } = action.payload
return { ...state, [name]: value }
}
case 'FILTER_RESET': {
return initialState
}
default:
return initialState
}
}
和我的actions.js
export const filtersApply = (payload) => ({
type: 'FILTER_UPDATE',
payload,
})
export const filtersReset = () => ({
type: 'FILTER_RESET',
})
解决方案
我广泛使用的模式之一(不仅是在 redux 中)是将列表保存在两个状态变量中。第一个是由某个标识符映射的所有实体的哈希映射,另一个是标识符列表。例如,您有酒店列表。我会这样做:
let initialState = {
hotels: {
1: {
id: 1,
name: "Marina Inn",
country: "Греция",
address: "Фалираки, Родос, Греция",
stars: 4,
type: "Отель",
description:
"Этот 4-звездочный отель расположен в центре города. На его территории есть бассейн с террасой и сауна. Из номеров открывается вид на море или на средневековый город.",
services: [
"Пляж",
"Кондиционер",
"Открытый бассейн",
"Бесплатная парковка",
"Бесплатный WiFi",
"Вид на море",
"Бесплатный завтрак",
],
min_price: 2789.0,
currency: "RUR",
rating: 4.5,
reviews_amount: 18,
last_review:
"Отличное расположение. Вкусный завтрак. Отдыхали с детьми - все понравилось.",
},
2: {
id: 2,
name: "Mondrian Suites",
country: "Греция",
address: "Фалираки, Родос, Греция",
stars: 5,
type: "Апартаменты",
description:
"Из окон открывается вид на город.К услугам гостей номера-студио с балконом и чайником в 2,7 км от пляжа.",
services: [
"Пляж",
"Кондиционер",
"Открытый бассейн",
"Платная парковка",
"Бесплатный WiFi",
"Вид на море",
],
min_price: 3245.2,
currency: "RUR",
rating: 5,
reviews_amount: 4,
last_review:
"Потрясающее место, в номере есть все необходимое. Красивый вид на море.",
},
3: {
name: "Sunny Appartments",
country: "Греция",
address: "Родос, Родос, Греция",
stars: 2,
type: "Апартаменты",
description:
"Все номера и апартаменты оборудованы кондиционером и телевизором с плоским экраном. Также в распоряжении гостей тостер и чайник.",
services: [
"Пляж",
"Кондиционер",
"Бесплатная парковка",
"Бесплатный WiFi",
],
min_price: 1153.0,
currency: "RUR",
rating: 2.4,
reviews_amount: 36,
last_review:
"Бассейн очень маленький. Кормят невкусно. Больше не поедем.",
},
4: {
name: "Super All Inclusive Hotel",
country: "Греция",
address: "Родос, Родос, Греция",
stars: 4,
type: "Отель",
description:
"Все номера оснащены телевизором с плоским экраном. Из некоторых номеров открывается вид на море или город.",
services: [
"Пляж",
"Кондиционер",
"Открытый бассейн",
"Бесплатный WiFi",
"Вид на море",
"Бесплатный завтрак",
],
min_price: 3689.0,
currency: "RUR",
rating: 4.1,
reviews_amount: 14,
last_review:
"Недалёко от пляжа и старого города, вокруг много разных магазинчиков",
},
5: {
name: "Adams Hotel",
country: "Греция",
address: "Родос, Родос, Греция",
stars: 3,
type: "Отель",
description:
"Отель расположен всего в 100 метрах от пляжа и в 5-ти минутах ходьбы от исторической части города, недалеко от всех основных достопримечательностей. Из отеля открывается вид на море. К услугам гостей спокойный открытый бассейн.",
services: [
"Пляж",
"Кондиционер",
"Открытый бассейн",
"Бесплатная парковка",
"Бесплатный WiFi",
"Бесплатный завтрак",
],
min_price: 1896.0,
currency: "RUR",
rating: 0,
reviews_amount: 0,
last_review: "",
},
},
ids: [1, 2, 3, 4, 5],
countryFilter: [],
typeFilter: [],
starsFilter: [],
reviewsAmountFilter: null,
};
现在,根据各种因素,我可以在 redux 内部或在我渲染它们的组件中执行过滤和排序。无论如何,我要排序的只是 ID 数组,我可以保持实际列表不变。例如,我想按星级范围过滤所有内容。减速器可能看起来像这样:
function reducer(state = initialState, event) {
if (event.type === "FILTER_UPDATE") {
// get fresh ids list
let ids = Object.keys(state.hotels);
let filteredAndSortedIds = ids
.filter((id) => {
let hotel = state.hotels[id];
// filter on parameters and return true or false
})
.sort((id1, id2) => {
/* sort */
});
// here you also can set filters and sorts if needed
return { ...state, ids: filteredAndSortedIds };
}
if (event.type === "FILTER_RESET") {
let ids = Object.keys(state.hotels).sort((id1, id2) => {
/* perform default sort to get consistent results */
});
// here you also can set default values for filters and sots
return { ...state, ids };
}
}
当您需要添加另一家酒店时,您可以在hotels
地图中添加新条目并在ids
数组中添加新条目。
您也可以不在 redux 内部,而是在组件渲染内部执行此类操作。您需要在这里小心,以免渲染您不想渲染的内容。在某些情况下,它会影响性能。
要将过滤器值发送到 redux,您已经拥有了所需的内容。只需扩展对象以包含所有值:dispatch(filterApply({'selectedCountries': countries, 'selectedTypes': types}))
等等。由于您将它们全部发送到 redux,您可能希望将它们保持在本地状态,而不是保存到 redux 存储中以避免复制和执行同步,但这取决于您。无论哪种情况,您都必须在 onClick 处理程序中将状态重置为初始状态,如下所示:
const [types, setTypes] = useState([]);
const [countries, setCountries] = useState([]);
<button
className="filter__reset"
onClick={() => {
filtersReset();
setTypes([]);
setCountries([]);
}}
>
Очистить фильтр
</button>;
推荐阅读
- kotlin - 如何使用 kotlinx.serialization 在 CBOR 对象和 ByteString 上添加 TAG
- python - python装饰器不返回函数值
- apache-flink - 在 Flink 中广播变量和作为参数传递有什么区别?
- amazon-s3 - 如何从 s3 服务器端加密客户提供的密钥转录
- vue.js - 如何在 VueJS 中调整 FullCalendar 窗口和单元格的大小?
- python - 如何使用 Numpy 中的等距样本对两个向量(V1,V2)描述的路径进行采样?
- excel - Excel VBA 运行时错误 9 下标超出范围
- php - 如何配置 session.save_path 并修复“会话启动期间的 phpMyAdmin 错误”?
- unity3d - Unity 中的 Vosk (Kaldi) 离线语音识别
- python - 在 groupby 中如何引用分组的列和值