Necesito hacer una lista de productos que se filtrarán al hacer clic. Pero no puedo entender cómo hacerlo en reaccionar. Y principalmente:

  1. ¿Dónde hacer la función de filtrado?
  2. ¿Cómo combinar y pasar los datos al reductor?
  3. Cómo activarlo todo con 1 botón.

Estoy seguro de que es un caso muy trivial para desarrolladores experimentados.

Repositorio con mi código hasta ahora: https://github.com/SolidMike/ react-hotels / tree / main / src /

Por ahora, tengo un componente que genera mi json y un componente que contiene todos los filtros. Lamentablemente, no puedo entender cómo hacer que funcione con mis acciones y reductores :(

Este es mi filtro

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 Debería incluso poner la matriz en el estado inicial, no estoy seguro.

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
    }
}

Y mis acciones.js

export const filtersApply = (payload) => ({
    type: 'FILTER_UPDATE',
    payload,
})

export const filtersReset = () => ({
    type: 'FILTER_RESET',
})
0
GeraltTheSLAYER 13 mar. 2021 a las 11:17

1 respuesta

La mejor respuesta

Uno de los patrones que utilizo ampliamente, y no solo con redux, es mantener listas en dos variables de estado. El primero es el mapa hash de todas las entidades asignadas por algún identificador, y otro es la lista de identificadores. Por ejemplo, tiene una lista de hoteles. Lo haría así:

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,
};

Ahora, dependiendo de varios factores, puedo realizar el filtrado y la clasificación dentro de redux o en el componente donde los renderizo. De todos modos, todo lo que tengo que ordenar es una matriz de ID, y puedo dejar la lista real sin cambios. Por ejemplo, quiero filtrar todo por rango de estrellas. El reductor podría verse así:

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 };
  }
}

Cuando necesite agregar otro hotel, agregue una nueva entrada a su mapa hotels y una nueva entrada en la matriz ids.

También puede realizar estas operaciones no dentro de redux, sino dentro de la representación del componente. Deberá tener cuidado aquí para no renderizar lo que no desea. En algunos casos, puede afectar el rendimiento.

Para enviar valores de filtros a redux, ya tiene lo que necesita. Simplemente amplíe el objeto para incluir todos los valores: dispatch(filterApply({'selectedCountries': countries, 'selectedTypes': types})) y así sucesivamente. Dado que los envía todos a redux, es posible que desee mantenerlos en el estado local y no guardarlos en el almacenamiento de redux para evitar duplicarlos y realizar la sincronización, pero el suyo depende de usted. En cualquier caso, tendrá que restablecer sus estados a la inicial dentro del controlador onClick, así:

const [types, setTypes] = useState([]);
const [countries, setCountries] = useState([]);

<button
  className="filter__reset"
  onClick={() => {
    filtersReset();
    setTypes([]);
    setCountries([]);
  }}
>
  Очистить фильтр
</button>;
1
Mr. Hedgehog 13 mar. 2021 a las 13:35