reactjs 如何在JavaScript / React中创建动态过滤器?

pgx2nnw8  于 2023-01-04  发布在  React
关注(0)|答案(2)|浏览(183)

我一直在尝试设置一个项目数组的过滤器,这样当用户点击复选框时,列表将只显示基于其选择的项目。它需要允许多个选择,以便多个条件中的任何一个(而不是全部)应用于过滤器。
下面的代码是我所拥有的,但它只将过滤器应用于第一个复选框,每次我选择第二个或第三个复选框时,过滤器都不会改变。

let filterOptions = document.querySelectorAll(".filter_checkbox")
var filterList = [];

const handleFilterCheckbox = (e) => {
     
    for(var i=0; filterOptions[i]; i++){
        if(filterOptions[i].checked){
            filterList.push(filterOptions[i].value);
        }
    }
    console.log(filterList)

    var getFilteredResults = hotelsList.filter(function (result) {
        for(var i=0; i < filterList.length; i++){
            return filterList.indexOf(result.locationName) >= 0;
        }
    }); 

    if(filterList.length > 0){
        setHotelsList(getFilteredResults)
    }else{
        setHotelsList(hotels)
    }

}

更新:下面是完整的代码

import { useState } from 'react'
import './cards.styles.css'
import Card from '../card/Card'
import hotels from '../../data/hotels_array'

export default function Cards(){

    const [hotelsList, setHotelsList] = useState(hotels)

    const handleSortChange = (e) => {
        const sortValue = e.target.value
        const copyHotelsList = [...hotelsList]

        if(sortValue === "sort-default"){
            setHotelsList(hotels)
        }else{
            copyHotelsList.sort((a, b) => {
                if(sortValue === "sort-price-lh"){
                    return a.fromPrice - b.fromPrice
                }else if (sortValue === "sort-price-hl"){
                    return b.fromPrice - a.fromPrice
                }else if(sortValue === "sort-review-score"){
                    return b.guestRating - a.guestRating
                }
            })
            setHotelsList(copyHotelsList)
        }
    }

    const hotelCards = hotelsList.map(hotel =>{

        return (
            <Card 
                key={hotel.hotelId}
                {...hotel}
            />
        )
    
    })

    const hotelsListUniqueByLocation = [...new Map(hotels.map(item => [item.locationName, item])).values()];

    let filterOptions = document.querySelectorAll(".filter_checkbox")
    var filterList = [];

    const handleFilterCheckbox = (e) => {
     
        for(var i=0; filterOptions[i]; i++){
            if(filterOptions[i].checked){
                filterList.push(filterOptions[i].value);
            }
        }
        console.log(filterList)

        var getFilteredResults = hotelsList.filter(function (result) {
            for(var i=0; i < filterList.length; i++){
                return filterList.indexOf(result.locationName) >= 0;
            }
        }); 

        if(filterList.length > 0){ 
            setHotelsList(getFilteredResults)
        }else{
            setHotelsList(hotels)
        }

    }

    const hotelLocations = hotelsListUniqueByLocation.map(location =>{

        if(location.locationName != ""){
            return (
                <li key={location.locationName}>
                    <input type="checkbox" id={location.locationName} value={location.locationName} onChange={handleFilterCheckbox} className="filter_checkbox" />
                    <label htmlFor={location.locationName} className='fs12p'>{location.locationName}</label>
                </li>
            )
        }
    })

    return(
        <div className="results_container">
            <aside>
                <h3>Filter</h3>
                Sorted by:
                <select onChange={handleSortChange}>
                    <option value="sort-default">Recommended</option>
                    <option value="sort-price-lh">Price (low to high)</option>
                    <option value="sort-price-hl">Price (high to low)</option>
                    <option value="sort-review-score">Review score</option>
                </select>
                <div>
                    <input type="checkbox" value="wififree" id="wififree" className="filter_checkbox" />
                    <label htmlFor="wififree" className='fs12p'>Free Wi-Fi</label>
                </div>
                <div>
                    <ul className='hotel_locations'>
                        {hotelLocations}
                    </ul>
                </div>
            </aside>
            <section className='cards_container container'>
                {hotelCards}
            </section>
        </div>
    
    )
}
okxuctiv

okxuctiv1#

我认为问题出在getFilteredResults
您将检查过滤器,但如果其中一个匹配,则返回true,因此不检查其他过滤器

for(var i=0; i < filterList.length; i++){
            return filterList.indexOf(result.locationName) >= 0;
        }

为了确保所有过滤器都匹配,可以使用every函数

var getFilteredResults = hotelsList.filter(hotel => 
    filterList.every(filter => filter === hotel.locationName)
 )
ymdaylpp

ymdaylpp2#

关键是将过滤器存储为状态,复选框会更新状态,然后可以使用函数生成相关数据:复选框的值,控制是否选中它们,以及酒店列表的对象。
我在您添加完整代码之前编写了此代码,因此它略有不同,但它应该显示复选框位置过滤器如何缩小酒店列表的范围。
注1:我没有在组件中使用函数,因为我可以将各种状态作为参数传递给它们,这主要意味着如果需要,可以将它们解耦到帮助文件中,使组件更简洁和干净。
注2:我在这里使用event delegation来捕获复选框事件,因为它们在DOM中"冒泡",但是向复选框添加单独的侦听器也是可以的--这样做更简洁一些。

const { Fragment, useState } = React;

// Filter the hotels by location
function filterHotels(hotels, filter) {
  return hotels.filter(hotel => {
    return filter.includes(hotel.location);
  });
}

// Get a deduped list of hotels by mapping their
// locations, adding them to a set, and sorting them
// alphabetically
function getLocations(hotels) {
  const locations = hotels.map(hotel => hotel.location);
  return [...new Set(locations)].sort();
}

// Works out if a checkbox is checked
function isChecked(filter, location) {
  return filter.includes(location);
}

// Based on the checkbox checked value return
// a new filter array with a checkbox value added, or a
// new filter array with a checkbox's value removed
function getUpdated(filter, value, checked) {
  return checked
    ? [...filter, value]
    : filter.filter(el => el !== value);
}

// For the purposes of this example I'm just passing in
// and array of predefined hotel objects
function Example({ config }) {

  // Initialise the hotels and filter states
  const [ hotels, setHotels ] = useState(config);
  const [ filter, setFilter ] = useState(getLocations(config));

  // The one function required in the component is the
  // one that updates the state. Because I've used event delegation
  // it first checks to see if the clicked element is a checkbox
  // and then sets the filter state with a new filter array using
  // the checkbox values
  function handleClick(e) {
    if (e.target.matches('[type="checkbox"]')) {
      const { value, checked } = e.target;
      setFilter(getUpdated(filter, value, checked));
    }
  }

  // Add some components that accepts forms of state
  // Note the click listener on the `section` element.
  // Using event delegation it catches events from its child
  // elements as they "bubble up" the DOM - the handler works out
  // if they're from the checkboxes and then processes the values
  return (
    <section onClick={handleClick}>
      <Filter locations={getLocations(hotels)} filter={filter} />
      <HotelList hotels={hotels} filter={filter} />
    </section>
  );

}

function Filter({ locations, filter }) {
  return (
    <section>
      {locations.map((location, id) => {
        return (
          <Fragment key={id}>
            <label htmlFor={location}>
              {location}
            </label>
            <input
              id={location}
              type="checkbox"
              value={location}
              checked={isChecked(filter, location)}
            />
          </Fragment>
        );
      })}
    </section>
  );
}

function HotelList({ hotels, filter }) {
  return (
    <ul>
      {filterHotels(hotels, filter).map(hotel => {
        return (
          <li key={hotel.id}>
            {hotel.name} ({hotel.location})
          </li>
        );
      })}
    </ul>
  );
}

const config=[{id:1,name:"York Hotel",location:"York"},{id:2,name:"London Hotel",location:"London"},{id:3,name:"Bork Hotel",location:"York"},{id:4,name:"Fancy Hotel",location:"London"},{id:5,name:"Pelican Hotel",location:"Paris"},{id:6,name:"Moose Hotel",location:"Paris"},{id:7,name:"Murder Hotel",location:"Chicago"}];

ReactDOM.render(
  <Example config={config} />,
  document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

相关问题