我正在嘗試使用 React-Hook-Form 和 React-Query 制作一個表單,只要用戶更改任何欄位(去抖動),它就會自動保存。我越來越接近了,但是當我變異時它會創建一個無限回圈。這是我所擁有的:
"@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 復制。你可以克隆 repo,運行 npm i , npm start,當你改變表單時你會看到問題。這是您需要查看的唯一頁面:
https://github.com/k-38/react-query_react-hook-form_autosave/blob/main/src/NameForm.tsx
任何幫助表示贊賞,謝謝
uj5u.com熱心網友回復:
因此,功能組件中的無限回圈useEffect通常是由于每個“回圈周期”上的依賴值都發生了變化。
在檔案中,我們可以閱讀:
watch 結果針對渲染階段而不是 useEffect 的 deps 進行了優化,為了檢測值更新,您可能需要使用外部自定義掛鉤進行值比較。
我懷疑(沒有時間查看代碼)watch在每次渲染時都會創建回傳值。然后fieldData在每個渲染上都是對新物件的參考。
大多數時候,我依賴onChange或onBlur形成事件。
function Form() {
const onChangeHandler = () => { /* ... */ };
return <form onChange={onChangeHandler}>
{/* ... */}
</form>
}
然后我使用useForm().getValues函式來檢索當前的表單值,但是您需要使用架構驗證來在其有效時觸發“自動保存”功能。
另一種解決方案(可能是更簡單的解決方案):將使用自定義掛鉤來深入比較這些值。useDeepCompareEffect你可以從react-use看看這個。
使用 debounce 功能的代碼中還有另一個錯誤: using
const debouncedFunction = debounce(myFunction, 500)將不起作用。“去抖”功能被記憶。由于我們在一個渲染函式(功能組件)中,記憶函式將在每個渲染上創建,所以它會被呼叫而不考慮你設定的閾值。
您需要為此使用React.useMemo:
const { mutateAsync } = mutationResult;
const handleDebouncedChange = React.useMemo(
() =>
debounce((data: NameType) => {
mutateAsync(data);
}, 500),
[mutateAsync]
);
此處作為代碼沙盒可用的完整作業版本將是:
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()
.required()
.min(2, "Must be greater than 1 character")
.max(50, "Must be less than 30 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, getValues, formState } = useForm<NameType>({
mode: "all",
criteriaMode: "all",
resolver: yupResolver(schemaValidation)
});
const { errors } = formState;
const fieldData = watch();
const { mutateAsync } = mutationResult;
const handleDebouncedChange = React.useMemo(
() =>
debounce((data: NameType) => {
mutateAsync(data);
}, 500),
[mutateAsync]
);
React.useEffect(() => {
reset(queryResult.data);
}, [queryResult.data]);
const onChange = async () => {
const data = getValues();
try {
console.log(formState);
const validated = await schemaValidation.validate(data);
handleDebouncedChange(validated);
} catch (e) {}
};
if (queryResult.isLoading) {
return <h2>Loading...</h2>;
}
return (
<form
style={{
display: "flex",
flexDirection: "column",
margin: "auto",
width: 300
}}
onChange={onChange}
>
<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)}
</form>
);
};
export default NameForm;
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/504132.html
標籤:javascript 反应 反应挂钩形式 反应查询
