过滤功能运行晚了一步



我正在构建一个组件来过滤数据数组。

有两个过滤器,一个用于团队,一个适用于位置。当每个下拉列表都更改时,我会更改团队和位置的状态变量,然后运行过滤器函数。

由于某种原因,筛选器运行得太晚了一步。

例如-如果我选择Team A,则不会更新任何内容。如果我选择了团队B,careersDataFiltered变量将显示团队A。如果我选择团队C,它将显示团队B的数据。

这几乎就像它跑得太晚了一步。

你可以从代码中看到,我在设置状态变量后运行过滤器,这就是为什么这对我来说有点令人头疼

import React, { useState } from "react"
import { motion, AnimatePresence } from "framer-motion"
const careersData = [
{
name: "X Job Title",
url: "/url/x",
team: "Team a",
location: "London",
},
{
name: "M Job Title",
url: "/url/m",
team: "Team a",
location: "London",
},
{
name: "B Job Title",
url: "/url/b",
team: "Team c",
location: "Sheffield",
},
{
name: "A Job Title",
url: "/url/a",
team: "Team b",
location: "London",
},
{
name: "F Job Title",
url: "/url/f",
team: "Team b",
location: "Sheffield",
},
{
name: "C Job Title",
url: "/url/c",
team: "Team c",
location: "London",
},
{
name: "Q Job Title",
url: "/url/q",
team: "Team a",
location: "Sheffield",
},
]
const uniqueTeams = []
const uniqueLocations = []
// Build the unique values
if (careersData !== null) {
careersData.map(career => {
if (uniqueTeams.indexOf(career.team) === -1) {
return uniqueTeams.push(career.team)
}
})
}
if (careersData !== null) {
careersData.map(career => {
if (uniqueLocations.indexOf(career.location) === -1) {
return uniqueLocations.push(career.location)
}
})
}
// reorder ready for output
uniqueTeams.sort()
uniqueLocations.sort()
const CurrentVacancies = () => {
const [careersDataFiltered, setCareersDataFiltered] = useState(careersData)
const [filterTeam, setFilterTeam] = useState("")
const [filterLocation, setFilterLocation] = useState("")
// filter array based on values
const runFilter = () => {
// reset the filter data
setCareersDataFiltered(careersData)
if (filterTeam !== "") {
setCareersDataFiltered(
careersDataFiltered.filter(career => career.team === filterTeam)
)
}
if (filterLocation !== "") {
careersDataFiltered(
careersDataFiltered.filter(career => career.location === filterLocation)
)
}
console.log(careersDataFiltered)
}
return (
<>
<div className="flex">
<h2 className="mr-auto">Current Vacancies</h2>
<div className="">
<select
onChange={e => {
setFilterTeam(e.target.value)
runFilter()
}}
>
<option value="">Team</option>
{uniqueTeams.map(team => (
<option key={team} value={team}>
{team}
</option>
))}
</select>
<select
onChange={e => {
setFilterLocation(e.target.value)
runFilter()
}}
>
<option value="">Location</option>
{uniqueLocations.map(location => (
<option key={location} value={location}>
{location}
</option>
))}
</select>
</div>
</div>
<div>
<AnimatePresence>
{careersDataFiltered.map((career, index) => (
<motion.div
key={index}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
positionTransition
>
<div>
<div className="text-sm">
{career.name} - {career.team} | {career.location}
</div>
</div>
</motion.div>
))}
</AnimatePresence>
</div>
</>
)
}
export default CurrentVacancies

首先,您的代码有一些错误,您应该在runFilter()的second-if语句中使用setCareersDataFiltered()。此外,setCareersDataFiltered()async,因此您不能在调用setCareersDataFiltered()之后立即使用careersDataFiltered。呼叫setFilterTeam()也是如此,因为您在runFilter()中使用filterTeam,所以在呼叫setFilterTeam()后无法呼叫runFilter(),因为您的数据不能保证更新。setFilterLocation()也是如此。

runFilter()放入useEffect()中,以便在runFilter()的任何依赖项发生变化时调用它。

useEffect(() => {
runFilter();
}, [runFilter]);

同时按如下方式更改runFilter(),并使用useCallback()来防止重新渲染循环。

// filter array based on values
const runFilter = useCallback(() => {
let filter = careersData;
if (filterTeam !== "") {
filter = filter.filter(career => career.team === filterTeam);
}
if (filterLocation !== "") {
filter = filter.filter(career => career.location === filterLocation);
}
setCareersDataFiltered(filter);
}, [filterLocation, filterTeam]);

最后,删除对runFilter()的任何手动调用;

作为提示,我认为您的过滤器逻辑有一个错误,因为我可以将团队设置为a,然后它只显示我a,但如果我设置位置,它将应用位置过滤器并忽略a过滤器,这是因为async问题。因此,如果您想同时应用两个筛选器,则应该在runFilter()中混合使用两个if语句。我混合了过滤器并编写了一个新的过滤器例程。

这是我的工作演示版

Ciao,这个问题可能与如何在函数runFilter中设置careersDataFiltered有关。根据我的个人经验,直接阅读并设置状态从来都不是一个好主意。尝试这样更改runFilter

const runFilter = () => {
let temp_careersDataFiltered = _.cloneDeep(careersData); //here you have to clone deep careersData because javascript keep reference (I use cloneDeep from lodash)
if (filterTeam !== "") {
temp_careersDataFiltered = careersData.filter(career => career.team === filterTeam)
}
if (filterLocation !== "") {
temp_careersDataFiltered = careersData.filter(career => career.location === filterLocation)
}
setCareersDataFiltered(
temp_careersDataFiltered 
)
console.log(temp_careersDataFiltered) 
}

说明:由于您不确定调用后careersDataFilteredsetCareersDataFiltered值是否立即可用,请对temp_careersDataFiltered临时数组和最终集合careersDataFiltered应用筛选器以重新渲染组件。我克隆careersData是因为您知道javascript可以与引用一起使用,如果您编写let temp_careersDataFiltered = careersData,如果您修改temp_careersDataFiltered,实际上您也在修改careersData

最新更新