英文:
react query. How get response on get request? I get only after second submit
问题
我正在创建一个搜索组件。我在请求中指定一个名称,然后得到一个响应并立即显示它。
在第一次提交输入后,响应中出现未定义,但在第二次提交后,我得到了我想要的结果。
我使用了React Query的useMutation钩子,React Hook Form的useForm,axios和M-UI。
useMutation钩子的profile.mutation.ts文件如下:
import { getUsersList } from '@/services/profileService/profileService'
import { IGetUsersList } from '@/services/types'
const profileConfig = {
getUsersList: {
key: 'getUsersList',
request: async (pageable: IGetUsersList) => {
const response = await getUsersList(pageable)
return response.data
},
},
}
export const useGetUsersListMutation = () => {
const { getUsersList: config } = profileConfig
const state = useMutation(config.request, {
retry: 10,
retryDelay: 1000,
onError(error) {
console.log('search error', error)
},
})
return state
}
带有问题的Search.tsx组件如下:
import React from 'react'
import InnerSearch from '@/components/search/InnerSearch'
import SettingsSVG from '@/assets/icons/Settings.svg'
import { Box, Container } from '@mui/material'
import { IGetUsersList } from '@/services/types'
import { useForm } from 'react-hook-form'
import { useGetUsersListMutation } from '@/query/profile/profile.mutation'
const Search = () => {
const { register, handleSubmit } = useForm<IGetUsersList>()
const { mutateAsync: mutateGetUsersList, isSuccess, data: resData } = useGetUsersListMutation()
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData); // 总是没有问题
mutateGetUsersList(requestData)
console.log('isSuccess', isSuccess); // 在第一次提交时:isSuccess为false,在第二次提交时:isSuccess为true
console.log('resData', resData); // 在第一次提交时:resData,在第二次提交时:resData为'right request'
};
return (
<Container disableGutters sx={{ padding: '0px 10px 0px 10px', }}>
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<InnerSearch register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit} />
<SettingsSVG />
</Box>
</Container>
)
}
export default Search
(InnerSearch组件包括:输入框和提交按钮)
我尝试使用async/await的onSubmit函数。
尝试在mutation中添加:
retry: 10,
retryDelay: 1000,
我尝试创建一个状态:
const [userlistArray, setUserlistArray] = useState([])
mutateGetUsersList(requestData).then((res) => { setUserlistArray(res) })
但是如果我执行console.log,然后在then
中得到了正确的结果!但是userlistArray是未定义的。再次说明,在第二次提交时一切都正常。
更新:感谢您的帮助!(我还需要添加状态)我的结果如下:
const Search = () => {
const [userlistArray, setUserlistArray] = useState([])
const { register, handleSubmit } = useForm<IGetUsersList>()
const { mutateAsync: mutateGetUsersList, data: resData } = useGetUsersListMutation()
const onSubmit = (requestData: IGetUsersList) => {
mutateGetUsersList(requestData)
};
useEffect(() => {
setUserlistArray(resData && resData.content)
}, [resData])
console.log('userlistArray____', userlistArray); //现在可以工作了!
return (
<Container></Container>
)
}
export default Search
英文:
I am making a search component. I specify a name in the request, I get a response and immediately display it.
After the first input submit I get undefined in response, but after the second submit a get what I wanted.
I use react query -> useMutation hook, react-hook-form -> useForm, axios, M-UI
hook useMutation profile.mutation.ts:
import { getUsersList } from '@/services/profileService/profileService'
import { IGetUsersList } from '@/services/types'
const profileConfig = {
getUsersList: {
key: 'getUsersList',
request: async (pageable: IGetUsersList) => {
const response = await getUsersList(pageable)
return response.data
},
},
}
export const useGetUsersListMutation = () => {
const { getUsersList: config } = profileConfig
const state = useMutation(config.request, {
retry: 10,
retryDelay: 1000,
onError(error) {
console.log('search error', error)
},
})
return state
}
component Search.tsx with bug:
import React from 'react'
import InnerSearch from '@/components/search/InnerSearch'
import SettingsSVG from '@/assets/icons/Settings.svg'
import { Box, Container } from '@mui/material'
import { IGetUsersList } from '@/services/types'
import { useForm } from 'react-hook-form'
import { useGetUsersListMutation } from '@/query/profile/profile.mutation'
const Search = () => {
const { register, handleSubmit } = useForm<IGetUsersList>();
const { mutateAsync: mutateGetUsersList, isSuccess, data: resData } = useGetUsersListMutation();
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData); //Object { username: "A" } always no problem
mutateGetUsersList(requestData)
console.log('isSuccess', isSuccess); //on first submit: isSuccess false, on second submit: isSuccess true
console.log('resData', resData); //on first submit: resData on second submit: resData 'right request'
};
return (
<Container disableGutters sx={{ padding: '0px 10px 0px 10px', }} >
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<InnerSearch register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit} />
<SettingsSVG />
</Box>
</Container>
)
}
export default Search
(component InnerSearch include: input and submit button)
I tried using onSubmit function acync&await.
tried add in mutation:
retry: 10,
retryDelay: 1000,
I tried to make a state:
const [userlistArray, setUserlistArray] = useState([])
mutateGetUsersList(requestData).then((res)=>{setUserlistArray(res)})
But if I execute console.log, then I get the correct result in then
! But userlistArray is undefined
. again, in the second submit everything is in order.
upd. Thank you for help! (Also I needed add state) My result:
const Search = () => {
const [userlistArray, setUserlistArray] = useState([])
const { register, handleSubmit } = useForm<IGetUsersList>();
const { mutateAsync: mutateGetUsersList, data: resData } = useGetUsersListMutation();
const onSubmit = (requestData: IGetUsersList) => {
mutateGetUsersList(requestData)
};
useEffect(()=>{
setUserlistArray(resData && resData.content);
}, [resData])
console.log('userlistArray____', userlistArray); //now it's work!
return (
<Container></Container>
)
}
export default Search
答案1
得分: 2
'isSuccess' 和 'resData' 不会立即更新的原因是由于 useMutation 钩子本身的异步性质。useMutation 是异步的,因此在 mutateAsync 调用后的同一 onSubmit 函数中直接记录日志时,isSuccess 和 resData 不会及时更新。
你应该依赖 useMutation 的 onSuccess 回调来处理实际可用时的数据,并使用 React Query 的状态标志(isLoading、isSuccess、isError 等)来处理界面更改。
export const useGetUsersListMutation = () => {
const { getUsersList: config } = profileConfig;
const state = useMutation(config.request, {
retry: 10,
retryDelay: 1000,
onError(error) {
console.log('search error', error);
},
// 添加此部分来处理成功
onSuccess(data) {
console.log('resData', data);
}
});
return state;
}
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData);
mutateGetUsersList(requestData);
// 这里删除了控制台日志,因为isSuccess和resData尚未更新
};
英文:
The reason why 'isSuccess' and 'resData' are not immediately updated is due to the nature of the useMutation hook itself. useMutation is asynchronous, and thus isSuccess and resData will not be updated in time to be logged directly after the mutateAsync call within the same onSubmit function.
You should instead rely on the onSuccess callback of useMutation to handle the data when it is actually available, and React Query's status flags (isLoading, isSuccess, isError, etc.) to handle UI changes.
export const useGetUsersListMutation = () => {
const { getUsersList: config } = profileConfig
const state = useMutation(config.request, {
retry: 10,
retryDelay: 1000,
onError(error) {
console.log('search error', error)
},
// add this to handle success
onSuccess(data) {
console.log('resData', data);
}
})
return state
}
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData);
mutateGetUsersList(requestData);
// removed console logs here because isSuccess and resData are not updated yet
};
答案2
得分: 1
RiQts 的答案是正确的,由于请求是异步的,控制台日志不会立即显示结果。然而一旦请求成功响应,resData
应该会触发组件的重新渲染,因此在 Search
函数中的控制台日志会显示无需再次触发突变,而是在请求成功完成后填充数据。
部分重现并运行的示例:https://codesandbox.io/s/zen-sunset-8r5389?file=/src/index.js
const Search = () => {
const { register, handleSubmit } = useForm<IGetUsersList>();
const { mutateAsync: mutateGetUsersList, isSuccess, data: resData } = useGetUsersListMutation();
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData); //Object { username: "A" } 总是没有问题
mutateGetUsersList(requestData)
};
// isSuccess 和 resData 应该在成功后自动更新
console.log('isSuccess', isSuccess);
console.log('resData', resData);
return (
<Container disableGutters sx={{ padding: '0px 10px 0px 10px' }}>
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<InnerSearch register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit} />
<SettingsSVG />
</Box>
</Container>
)
}
编辑:我知道我有点跑题了,但这是另一种编写突变钩子的方法,使用状态来管理触发 useQuery
,无论如何看起来都不错。
const Search = () => {
// 将此状态传播到需要userListData的任何组件
const [searchQueryData, setSearchQueryData] = useState<IGetUserList>(null);
const { isSuccess, data: resData } = useGetUsersListQuery(searchQueryData);
// ... 更新表单中的 searchQueryData 以重新获取
return (
// ...
);
}
const useGetUsersListQuery = (searchQueryData) => {
return useQuery({
queryKey: ['profilesList', searchQueryData],
queryFn: async () => { const response = await getUsersList(searchQueryData); return response.data; },
keepPreviousData: true,
enabled: searchQueryData !== null,
});
};
英文:
RiQts answer is correct in that due to the requests asynchrony the console logs do not immediately show the result. However once the request responses successfully resData should trigger a re-render of the component, so console logging in the Search function should show that there is no need to trigger the mutation again but that the data gets filled after the request successfully finishes
Somewhat reproduced and working https://codesandbox.io/s/zen-sunset-8r5389?file=/src/index.js
const Search = () => {
const { register, handleSubmit } = useForm<IGetUsersList>();
const { mutateAsync: mutateGetUsersList, isSuccess, data: resData } = useGetUsersListMutation();
const onSubmit = (requestData: IGetUsersList) => {
console.log(requestData); //Object { username: "A" } always no problem
mutateGetUsersList(requestData)
};
// isSuccess and resData should automatically update after success
console.log('isSuccess', isSuccess);
console.log('resData', resData);
return (
<Container disableGutters sx={{ padding: '0px 10px 0px 10px', }} >
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 4 }}>
<InnerSearch register={register}
handleSubmit={handleSubmit}
onSubmit={onSubmit} />
<SettingsSVG />
</Box>
</Container>
)
}
Edit: I know I'm a bit off topic here, but here is an alternate way to write the mutation hook, use a state to manage the triggering of the useQuery, anyway just my two cents. Looking good in any case
const Search = () => {
// spread this state to any component that needs the userListData
const [searchQueryData, setSearchQueryData] = useState<IGetUserList>(null);
const { isSuccess, data: resData } = useGetUsersListQuery(serchQueryData);
// ... update the searchQueryData in the form to refetch
return (
// ...
);
}
const useGetUsersListQuery = (serchQueryData) => {
return useQuery({
queryKey: ['profilesList', serchQueryData],
queryFn: async () => { const response = await getUsersList(searchQueryData); return response.data; },
keepPreviousData: true,
enabled: searchQueryData !== null,
});
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论