【问题标题】:How do I filter multiple items on React?如何在 React 上过滤多个项目?
【发布时间】:2020-05-11 05:33:09
【问题描述】:

我是 React 的新手,我正在努力在 React 上应用多个过滤器。我需要按以下条件过滤应用过滤器的车辆列表:年份、价格范围、新旧车辆。我不知道该怎么做,我有点迷失了搜索和观看教程。有人可以帮我吗?

我可以单独应用每个过滤器,但不能一起应用。我正在考虑在我的代码中添加一种过滤器。

这是我的代码:

数据:

const faixaValor = useMemo(
    () => [
      { ID: 1, PriceRangeID: 1, value: '0-20', Name: 'R$0 - R$20.000' },
      { ID: 2, PriceRangeID: 2, value: '20-40', Name: 'R$20.001 - R$40.000' },
      { ID: 3, PriceRangeID: 3, value: '40-60', Name: 'R$40.001 - R$60.000' },
      { ID: 4, PriceRangeID: 4, value: '60-100', Name: 'R$60.001 - R$100.000' },
    ],
    []
)

const anos = useMemo(
    () => [
        { ID: 1, YearID: 1, value: 2012, Name: '2012' },
        { ID: 2, YearID: 2, value: 2013, Name: '2013' },
        { ID: 3, YearID: 3, value: 2014, Name: '2014' },
        { ID: 4, YearID: 4, value: 2015, Name: '2015' },
        { ID: 5, YearID: 5, value: 2016, Name: '2016' },
        { ID: 6, YearID: 6, value: 2017, Name: '2017' },
        { ID: 7, YearID: 7, value: 2018, Name: '2018' },
    ],
    []
)

我的复选框:

<Checkbox 
         value={newChecked} 
         onChange={event => setNewChecked(event.target.checked)} 
         title="Novos"
         />
<Checkbox 
         value={usedChecked} 
         onChange={event => setUsedChecked(event.target.checked)} 
         title="Usados"
         />

搜索组件传递props:

<Search 
       priceRange={priceRange}
       year={year}
       newChecked={newChecked} 
       usedChecked={usedChecked}
/>

搜索组件:

function Search(props) {

const [loading, setLoading] = useState(true)
const [error, setError] = useState(false)
const [hasMore, setHasMore] = useState(false)
const [pageNumber, setPageNumber] = useState(1)
const [vehicles, setVehicles] = useState([])
const [search, setSearch] = useState(false)
const [filteredVehicles, setFilteredVehicles] = useState([])
const [filters, setFilters] = useState([])
const apiService = ApiService

// Infinite loading by page - 10 in 10 vehicles
useEffect(() => {
    setLoading(true)
    setError(false)
    getVehicles()        
}, [search, pageNumber])

useEffect(() => {
    filterVehicles(vehicles)
}, [props])

// Observe page position
const observer = useRef()
const lastVehicleElement = useCallback(node => {
    if(loading) 
    return 
    if(observer.current) observer.current.disconnect()
    observer.current = new IntersectionObserver(entries => {
        if(entries[0].isIntersecting && hasMore) {
            setPageNumber(prevPageNumber => prevPageNumber + 1)
        }
    })
    if(node) observer.current.observe(node)
}, [loading, hasMore])


async function getVehicles() {

    await apiService.ListVehicle(pageNumber)
        .then(res => {
            setVehicles(prevVehicles => {
                if(isFiltered) {
                    return [...new Set([...prevVehicles, ...res])]
                } else {
                    return null
                }
            })
            setHasMore(res.length > 0)
            setLoading(false)
        })
        .catch(err => {
            setError(true)
            throw new Error (err.message)
        })
}

async function filterVehicles(vehicles) {
    /* Radius - No need 'cause of the API
    if(props.radius) {}
    */

    // Year
    if(props.year) {
        const filterYear = await vehicles.filter(
            vehicle => (vehicle.YearFab == props.year)
        )
        await setFilters(prev => ({ ...prev, year: true }))
        await setFilteredVehicles(filterYear)
        console.log(filters)
    }

    // Price Range
    if(props.priceRange) {

        let prices = props.priceRange.split('-')
        let priceMin = parseInt(prices[0])
        let priceMax = parseInt(prices[1])

        const filterMin = await vehicles.filter(
            vehicle => ((parseInt(vehicle.Price)/1000) > priceMin)
        )
        const finalFilter = await filterMin.filter(
            vehicle => ((parseInt(vehicle.Price)/1000) < priceMax)
        )
        await setFilteredVehicles(finalFilter)
        await setFilters(prev => ({ ...prev, priceRange: true }))
        console.log(filters)
    }

    // Both checkboxes true
    if(!props.newChecked & !props.usedChecked) {
        return null
    } else {
        // Checkbox new True
        if(props.newChecked & !props.usedChecked) {
            const filterNew = await vehicles.filter(
                vehicle => !vehicle.KM
            ) 
            await setFilteredVehicles(filterNew)
            await setFilters(prev => ({ ...prev, filterNew: true }))
        }
        // Checkbox used True
        if(props.usedChecked & !props.newChecked) {
            const filterUsed = await vehicles.filter(
                vehicle => vehicle.KM
            )
            await setFilteredVehicles(filterUsed)
            await setFilters(prev => ({ ...prev, filterUsed: true }))
        }
    } 
    if(props.newChecked & props.usedChecked) {
        await setFilteredVehicles(vehicles)
        await setFilters([])
    }        
}

function isFiltered() {

    if(props.newChecked || props.usedChecked || props.year || props.priceRange) {
        return true
    } else {
        return false
    }
}

async function handleClick() {

    setVehicles([])

    let totalPages = 3;
    for(let i=1; i <= totalPages; i++) {
        await apiService.ListVehicle(i)
            .then(res => {
                setVehicles(prevVehicles => {  
                    return [...new Set([...prevVehicles, ...res])] 
                })
            })
    }
    filterVehicles(vehicles)
    setVehicles(filteredVehicles)
    setSearch(true)        
}

async function handleFilter() {
    setVehicles([])
    setFilteredVehicles([])
}

return (
    <Fragment>
        <button onClick={() => handleClick()}>Buscar</button>  
        <button onClick={() => handleFilter()}>Limpa</button> 

        { isFiltered() ? (
            <div>
            <h1 className="filtered">Filtrados</h1>
            <div className="grid">
                { filteredVehicles.map(vehicle => (
                    <div className="vehicle-card-item" key={vehicle.ID}>
                        <ul>
                            <li>
                                <img src={vehicle.Image} alt={vehicle.Make}/>
                                <div className="vehicle-details">
                                    <div className="vehicle-main-info">
                                        <h2>{vehicle.Make} {vehicle.Model}</h2>
                                        <h3>{vehicle.Version}</h3>
                                    </div>
                                    <div className="vehicle-price">
                                        <strong>R${vehicle.Price}</strong>
                                    </div>
                                    <div className="year-and-km">
                                        <h4>{vehicle.YearFab} / {vehicle.YearModel}</h4>
                                        <h3>{vehicle.KM} km</h3> 
                                    </div>
                                </div>
                            </li>
                        </ul>
                    </div>   
                ))
                }
            </div>
            </div>
        ) : (
            // { search &&
            <div className="grid">
                { vehicles.map((vehicle, index) => { 
                    if (vehicles.length === index + 1) { 
                    return <div className="vehicle-card-item" ref={lastVehicleElement} key={vehicle.ID}>
                        <ul>
                            <li>
                                <img src={vehicle.Image} alt={vehicle.Make}/>
                                <div className="vehicle-details">
                                    <div className="vehicle-main-info">
                                        <h2>{vehicle.Make} {vehicle.Model}</h2>
                                        <h3>{vehicle.Version}</h3>
                                    </div>
                                    <div className="vehicle-price">
                                        <strong>R${vehicle.Price}</strong>
                                    </div>
                                    <div className="year-and-km">
                                        <h4>{vehicle.YearFab} / {vehicle.YearModel}</h4>
                                        <h3>{vehicle.KM} km</h3> 
                                    </div>
                                </div>
                            </li>
                        </ul> 
                        { loading ? ( <Loading /> ): null} 
                    </div> 
                    } else {
                    return <div className="vehicle-card-item" key={vehicle.ID}>
                        <ul>
                            <li>
                                <img src={vehicle.Image} alt={vehicle.Make}/>
                                <div className="vehicle-details">
                                    <div className="vehicle-main-info">
                                        <h2>{vehicle.Make} {vehicle.Model}</h2>
                                        <h3>{vehicle.Version}</h3>
                                    </div>
                                    <div className="vehicle-price">
                                        <strong>R${vehicle.Price}</strong>
                                    </div>
                                    <div className="year-and-km">
                                        <h4>{vehicle.YearFab} / {vehicle.YearModel}</h4>
                                        <h3>{vehicle.KM} km</h3> 
                                    </div>
                                </div>
                            </li>
                        </ul>  
                    </div> 
                    }
                })}
            </div>
        )}     
    </Fragment>
)

}

export default Search

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    我稍微简化了一点,将 API 逻辑和搜索选择 JSX 排除在外。这个想法是您不要尝试将过滤后的车辆列表设置为状态,而是在每次渲染元素时计算它。

    const vehicles = [
      {name: "foo1", year: 2020},
      {name: "foo2", year: 2020},
      {name: "bar1", year: 2019},
      {name: "bar2", year: 2019},
      {name: "baz1", year: 2018},
      {name: "baz2", year: 2018},
    ];
    
    const SearchWithFilter = () => {
      // Should be set to number if we want only vehicles from that
      // year, or null if it is disabled.
      const [yearFilter, setYearFilter] = React.useState(null)
      const [nameFilter, setNameFilter] = React.useState(null)
    
      const applyYearFilter = (vehicle) => {
        // Keep all cars if the year filter is not set, otherwise
        // only keep the cars which meet the year filter.
        return !yearFilter || yearFilter === vehicle.year
      }
    
      const applyNameFilter = (vehicle) => {
        return !nameFilter || vehicle.name.includes(nameFilter)
      }
    
      const filteredVehicles = vehicles
           .filter(applyYearFilter) // Remove all vehicles which do not match the year filter
           .filter(applyNameFilter) // Remove all vehicles which do not match the name filter
           .map((vehicle) => <p>{vehicle.name}</p>) // Create JSX for each vehicle object
    
      return (
            <div>
             <h1>Filter on year</h1>
             <button onClick={() => setYearFilter(2020)}>2020</button>
             <button onClick={() => setYearFilter(2019)}>2019</button>
             <button onClick={() => setYearFilter(null)}>Clear</button>
             <h1>Filter on name:</h1>
             <input onChange={(event) => setNameFilter(event.target.value)} />
             <h1>Results:</h1>
             {filteredVehicles}
          </div>
      )
    }
    

    https://jsfiddle.net/Addono/1pd2a0ob/

    【讨论】:

    • 抱歉,我没有正确理解回复延迟。但我在想也许可以尝试使用 REDUX 来简化状态管理。然后我想我也会简化过滤方法。我对吗? (我还在学习REDUX,不知道我的说法是否正确)
    • @WilliamS.Takayama Redux 不是灵丹妙药:redux.js.org/faq/general#when-should-i-use-redux。对我来说,你的问题似乎很简单,可以用 vanilla React 解决,因为你的状态相对简单。我认为改进您的解决方案的关键是将您的状态限制为过滤器的配置。您的原始解决方案还尝试将计算结果存储在状态中,这对您的状态来说是不必要的额外复杂性。
    • 无论如何,我实现了一个完整的例子。它只有两个过滤器,但我希望模式清晰并允许您创建自己的过滤器。
    • 非常感谢!它向我澄清了我的解决方案真的很混乱,我迷路了......
    猜你喜欢
    • 2014-12-19
    • 2020-11-03
    • 1970-01-01
    • 2016-10-01
    • 2020-04-21
    • 2021-03-28
    • 1970-01-01
    • 2011-06-08
    • 2021-09-15
    相关资源
    最近更新 更多