React-Query结合React-Hook-Form防抖自动保存遇无限循环问题求助
问题:React-Hook-Form + React-Query 防抖自动保存出现无限循环
我正尝试用React-Hook-Form和React-Query实现一个表单,用户修改任意字段时触发防抖自动保存。目前功能接近完成,但执行mutation时出现了无限循环问题。
依赖版本
"@tanstack/react-query": "^4.2.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.34.2",
问题代码
import React from 'react' import { useMutation, useQuery } from '@tanstack/react-query' import { useForm } from 'react-hook-form' import { yupResolver } from '@hookform/resolvers/yup' import * as Yup from 'yup' import debounce from 'just-debounce-it' type NameType = { id: number firstName: string lastName: string } const schemaValidation = Yup.object().shape({ id: Yup.number().required('Required'), firstName: Yup.string().required('Required'), lastName: Yup.string() .min(2, 'Must be greater than 1 character') .max(50, 'Must be less than 50 characters') }) const getMockData = async () => { const name: NameType = { id: 1, firstName: 'John', lastName: 'Doe' } return await Promise.resolve(name) } const saveChangeToDatabase = async (args: NameType) => { console.count('payload for patch:' + JSON.stringify(args)) return await Promise.resolve(args) } const NameForm = () => { const queryResult = useQuery(['user'], getMockData) const mutationResult = useMutation(saveChangeToDatabase, { onSuccess: (nameToSave: NameType) => { console.count('success mutating: ' + JSON.stringify(nameToSave)) } }) const { register, reset, watch, formState: { isValid, isDirty, errors } } = useForm<NameType>({ mode: 'all', criteriaMode: 'all', resolver: yupResolver(schemaValidation) }) const fieldData = watch() const handleDebouncedChange = debounce((data: NameType) => { mutationResult.mutateAsync(data) }, 500) React.useEffect(() => { reset(queryResult.data) }, [queryResult.data]) React.useEffect(() => { if (isValid && isDirty) { handleDebouncedChange(fieldData) } }, [fieldData, isValid, isDirty]) if (queryResult.isLoading) { return <h2>Loading...</h2> } return ( <div style={{ display: 'flex', flexDirection: 'column', margin: 'auto', width: 300 }}> <input {...register('firstName')} placeholder='First name' /> <div style={{ color: 'red' }}>{errors && errors?.firstName?.message}</div> <input {...register('lastName')} placeholder='Last name' /> <div style={{ color: 'red' }}>{errors && errors?.lastName?.message}</div> {'Field data: ' + JSON.stringify(fieldData)} </div> ) } export default NameForm
复现方式
已基于create-react-app创建复现项目,克隆后执行npm i、npm start,修改表单即可触发无限循环问题。
更新:抽象自定义Hook
感谢解答,我已根据答案抽象出一个自定义Hook(暂未添加类型):
import React from 'react' import debounce from 'just-debounce-it' export function useDebouncedAutoSave({ mutationResult, validationSchema, getValues, reset, queryResult }: { mutationResult: any validationSchema: any getValues: any reset: any queryResult: any }) { React.useEffect(() => { reset(queryResult.data) }, [queryResult.data]) const handleDebouncedChange = React.useMemo( () => debounce((data: any) => { mutationResult.mutateAsync(data) }, 500), [mutationResult.mutateAsync] ) const onChange = async () => { try { const values = getValues() const validated = await validationSchema.validate(values) if (!validated) return handleDebouncedChange(validated) } catch (e) {} } return { onChange } }
内容的提问来源于stack exchange,提问作者Jar
相关产品推荐
相关产品推荐

