简体   繁体   English

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

[英]How can I create a dynamic filter in JavaScript / React?

I have been trying to set up a filter on an array of items so that when the user clicks a checkbox, the list will display only items based on their selection.我一直在尝试在项目数组上设置过滤器,以便当用户单击复选框时,列表将仅显示基于他们选择的项目。 It needs to allow for multiple selections so that ANY (not ALL) of the multiple conditions are applied to the filter.它需要允许进行多项选择,以便将多个条件中的任何(而不是全部)应用于过滤器。

The code below is what I have, but it only applies the filter to the first checkbox, everytime I select a second, or third checkbox, the filter doesn't change.下面的代码是我所拥有的,但它只将过滤器应用于第一个复选框,每次我 select 第二个或第三个复选框时,过滤器都不会改变。

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

}

UPDATE: Below is the full code更新:下面是完整的代码

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

The key is to store the filter as state. Your checkboxes update that state, and then you can use functions to produce the relevant data: values for your checkboxes, and to control whether they're checked or not, and objects for your hotel list.关键是将过滤器存储为 state。您的复选框更新 state,然后您可以使用函数生成相关数据:复选框的值,并控制它们是否被选中,以及酒店列表的对象.

I wrote this before you added your full code, so it differs a little, but it should show how checkbox location filters can narrow down a list of hotels.我在您添加完整代码之前写了这篇文章,所以它有点不同,但它应该显示复选框位置过滤器如何缩小酒店列表。

Note 1: I don't have the functions inside the component because I can pass through the various states to them as arguments. Primarily it means you can decouple them into helper files if needed to make your components a little more concise and clean.注意 1:我没有组件内部的功能,因为我可以将各种状态传递给它们,如 arguments。主要是说,如果需要,您可以将它们分离到帮助文件中,以使您的组件更加简洁和干净。

Note 2: I'm using event delegation here to catch checkbox events as they "bubble up" the DOM, but adding individual listeners to the checkboxes is fine too - this is just a little more succinct.注意 2:我在这里使用事件委托来捕获复选框事件,因为它们“冒泡”了 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>

I think the problem is in the getFilteredResults我认为问题出在getFilteredResults

you are going over the filters but in case one of them matches, you return true and by that, not checking the other filters您正在检查过滤器,但如果其中一个匹配,则返回true ,这样就不会检查其他过滤器

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

What you can do in order to make sure all your filters are match, is to use the every function为了确保所有过滤器都匹配,您可以做的是使用every function

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM