使用 redux 在点击时应用和重置多个过滤器

时间:2021-03-13 08:17:56

标签: reactjs redux react-redux

我需要制作将在点击时过滤的产品列表。但我只是无法掌握如何在反应中做到这一点。主要是:

  1. 在哪里做过滤功能?
  2. 如何组合并将数据传递给 reducer?
  3. 如何用 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',
})

1 个答案:

答案 0 :(得分:1)

我广泛使用的模式之一,不仅是 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 数组,并且我可以保持实际列表不变。例如,我想按星星范围过滤所有内容。 Reducer 可能看起来像这样:

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>;
相关问题