react-query useQuery在异步抓取时跳过条目



在一个高层次的概述,我正在建立一个类似tinder应用程序的功能。在我的示例中,从数据库中获取4个条目,每次显示一个,并可以单击喜欢或不喜欢按钮。每次单击按钮都会触发一些异步函数,用于将事件写入数据库。一旦最后一个被获取的条目被点击,我需要去取下一个4条目在我的数据库。

<<p>我的组件/strong>
export const HomePage: React.FC = () => {
const { user } = useUser()
const userId = user?.id
const [current, setCurrent] = useState<number | null>(0)
const [skip, setSkip] = useState(0)
const [url, setUrl] = useState(
`/api/swipes/get-users/?userId=${user?.id}&skip=${skip}`
)
const getUsers = async (url: string) => {
const { data } = await axios.get(url)
return data
}
const { error, data, isLoading, refetch } = useQuery(
['userSwipeData', url],
() => getUsers(url),
{
enabled: !!user?.id,
}
)
const handleRefetchUsers = () => {
setCurrent(0)
setUrl(
`/api/swipes/get-users/?userId=${user?.id}&skip=${
skip + FETCH_USERS_PAGINATION_LIMIT
}`
)
refetch()
setSkip(skip + FETCH_USERS_PAGINATION_LIMIT)
}
const handleSwipe = async (e: any) => {
if (current === null || !data) return
const { value } = e.target
await handleSwipeType(value, data?.users[current].id)
}
const handleSwipeType = async (type: string, id: string) => {
const values = {
userSwipeOn: id,
currentUser: userId,
}
// if (type === 'YES') {
//   if (current && data?.users[current]?.isMatch) {
//     await axios.post('/api/swipes/create-match', values)
//     alert('You have a match!')
//   }
//   await axios.post('/api/swipes/like', values)
// } else {
//   await axios.post('/api/swipes/dislike', values)
// }
if (current === data.users.length - 1) {
handleRefetchUsers()
} else {
setCurrent(current! + 1)
}
}
if (isLoading || current === null) return <Box>Fetching users...</Box>
if (error) return <Box>An error has occurred </Box>
if (data && !data?.users.length) return <Box>No more users for now</Box>
return (
<Box>
<h1>Current User: {data?.users[current].email}</h1>
<Flex>
<button value="NO" onClick={handleSwipe}>
NO
</button>
<button value="YES" onClick={handleSwipe}>
YES
</button>
</Flex>
</Box>
)
}

我面临的问题是,在当前状态下,当handleRefetchUsers()函数触发它按预期工作。但是,如果我要取消注释所有需要在每次点击时运行以记录事件的异步代码,一旦handleRefetchUsers()触发,我注意到它每次运行时都会跳过4个条目。我真的很困惑为什么,因为检查最终条目应该只在异步代码完成后运行。如果有什么建议,我会很有帮助的。

我很确定refetch不会等待setUrl实际更新url

你不应该把一个状态建立在另一个状态之上

要解决这个问题,我将替换

const [url, setUrl] = useState(
`/api/swipes/get-users/?userId=${user?.id}&skip=${skip}`
)

const url = /api/swipes/get-users/?userId=${user?.id}&skip=${skip}`

并完全移除refetch。因为url改变了,React-query会重新获取

有一些事情可以改进,但你的主要问题是setState是异步的。因此,当您使用setUrl然后调用refetch时,refetch仍然会查看旧的url值。

我认为一个更干净的方法是使用refetch内的效果,有currentskip在依赖数组。

另外,url是一个派生状态,所以它不需要自己的状态。当一个新状态依赖于前一个状态时,你也应该使用一个箭头函数——同样,因为setState是异步的,你有可能引用一个旧的状态。

const buildUrl = (user, skip) => user?.id ? `/api/swipes/get-users/?userId=${user?.id}&skip=${skip}` : ''
export const HomePage: React.FC = () => {
const { user } = useUser()
const userId = user?.id
const [current, setCurrent] = useState<number>(0) // current page
const [skip, setSkip] = useState(0)
const getUsers = async (url: string) => {
const { data } = await axios.get(url)
return data
}
const { error, data, isLoading, refetch } = useQuery(
['userSwipeData', buildUrl(user, skip)],
() => getUsers(buildUrl(user, skip)),
{
enabled: !!user?.id,
}
)
const handleRefetchUsers = () => {
setCurrent(0)
setSkip((prev) => prev + FETCH_USERS_PAGINATION_LIMIT)
}
const handleSwipe = async (e: any) => {
if (current === null || !data) return
const { value } = e.target
await handleSwipeType(value, data?.users[current].id)
}
const handleSwipeType = async (type: string, id: string) => {
const values = {
userSwipeOn: id,
currentUser: userId,
}
if (type === 'YES') {
if (current && data?.users[current]?.isMatch) {
await axios.post('/api/swipes/create-match', values)
alert('You have a match!')
}
await axios.post('/api/swipes/like', values)
} else {
await axios.post('/api/swipes/dislike', values)
}
if (current === data.users.length - 1) {
handleRefetchUsers()
} else {
setCurrent((prev) => prev + 1)
}
}
useEffect(() => {
refetch()
}, [current, skip])
if (isLoading || current === null) return <Box>Fetching users...</Box>
if (error) return <Box>An error has occurred </Box>
if (data && !data?.users.length) return <Box>No more users for now</Box>
return (
<Box>
<h1>Current User: {data?.users[current].email}</h1>
<Flex>
<button value="NO" onClick={handleSwipe}>
NO
</button>
<button value="YES" onClick={handleSwipe}>
YES
</button>
</Flex>
</Box>
)
}

最新更新