[英]useEffect is not updating the default value of react-hook-form when creating edit form
[英]Edit form with custom component for react-hook-form : default value
我學習 ReactJS + TypeScript 已經 3 個月了。我的問題是關於使用 react-hook-form (v7) 編輯表單。 我想使用我創建的自定義組件,並找到了如何自己做!
這是我的表單提供者的一部分,帶有 react-hook-form
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import InputText from 'components/commons/form/InputText';
import { supabase } from 'configs/supabase';
const EditEducation: React.FC = () => {
const { educationId } = useParams();
const [education, setEducation] = useState<education>();
const getEducation = async (educationId: string | undefined) => {
try {
const { data, error } = await supabase
.from('tables1')
.select('data1, data2')
.eq('id', educationId)
.single();
if (error) {
seterror(error.message);
}
if (data) {
return data;
}
} catch (error: any) {
alert(error.message);
}
};
useEffect(() => {
getEducation(educationId).then((data) => {
setEducation(data);
});
// eslint-disable-next-line
}, [educationId]);
const methods = useForm();
const onSubmit = async (formData: any) => {
const updateData = {
data1 = formData.data1,
data2 = formData.data2
};
try {
setSaving(true);
const { error } = await supabase.from('educations').update(updateData);
if (error) {
seterror(error.message);
}
if (!error) {
navigate('/experiences/education');
}
setSaving(false);
} catch (error: any) {
seterror(error.message);
}
};
return (
...
<FormProvider {...methods}>
<form className="p-4" onSubmit={methods.handleSubmit(onSubmit)}>
<InputText
id="data1"
label="Data1"
placeholder="Ex: data1"
defaultValue={education?.data1}
options={{ required: 'This field is required' }}
/>
<Button type="submit">{saving ? 'Saving' : 'Save'}</Button>
</form>
</FormProvider>
...
)
};
這是我的自定義組件:
import React, { useEffect } from 'react';
import { useFormContext } from 'react-hook-form';
interface InputProps {
id: string;
label: string;
placeholder?: string;
defaultValue?: string;
}
const InputText: React.FC<InputProps> = ({
id,
label,
placeholder,
defaultValue,
options,
...rest
}: InputProps) => {
const {
register,
setValue,
formState: { errors }
} = useFormContext();
useEffect(() => {
if (defaultValue) setValue(id, defaultValue, { shouldDirty: true });
}, [defaultValue, setValue, id]);
return (
<div className="">
<label htmlFor={id} className="">
{label}
</label>
<input
type="text"
placeholder={placeholder}
className=""
id={id}
defaultValue={defaultValue}
{...register(id, options)}
{...rest}
/>
{errors[id] && (
<p className="">
<span className="">*</span> {errors[id]?.message}
</p>
)}
</div>
);
};
export default InputText;
如您所見,我使用了 formContext 因為我想將我的代碼解構為更小的組件。
現在我有點懷疑我是否正確編碼,特別是當我使用 ut 編輯 forms 時:如果通過“defaultValue”prop 設置我的默認值,我必須提交(錯誤顯示)然后在輸入中 clique 以按順序更改 state清除輸入組件中的錯誤。
這就是為什么我添加了 useEffect 鈎子來清除輸入驗證錯誤並且它正在工作。 你怎么看待這件事? 有沒有更好的方法來管理它(我認為是的,這是設置驗證模式的更簡潔的方法)?
提前致謝,抱歉我生疏的英語。 祝大家度過美好的一天,希望我的代碼能對人們有所幫助。
使用 <FormProvider {...methods}> 並且它正在工作,但我不知道這是否是一個好方法。
編輯:實際上,我必須兩次提交才能獲取我的數據,所以我想這不是正確的方法,有什么建議嗎?
Edit2 :我找到了一個“解決方案”:如果我的道具中有一個默認值,我會在我的組件中做:
useEffect(() => {
if (defaultValue) setValue(id, defaultValue, { shouldDirty: true });
}, [defaultValue, setValue, id]);
我不認為這是更好的解決方案......
我學習 ReactJS + TypeScript 已經 3 個月了。我的問題是關於使用 react-hook-form (v7) 編輯表單。 我想使用我創建的自定義組件,並找到了如何自己做!
editForm.tsx
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import InputText from 'components/commons/form/InputText';
import { supabase } from 'configs/supabase';
const EditEducation: React.FC = () => {
const { educationId } = useParams();
const [education, setEducation] = useState<education>();
...
const getEducation = async (educationId: string | undefined) => {
try {
const { data, error } = await supabase
.from('tables1')
.select('data1, data2')
.eq('id', educationId)
.single();
if (error) {
seterror(error.message);
}
if (data) {
return data;
}
} catch (error: any) {
alert(error.message);
}
};
useEffect(() => {
getEducation(educationId).then((data) => {
setEducation(data);
});
// eslint-disable-next-line
}, [educationId]);
const methods = useForm();
const onSubmit = async (formData: any) => {
const updateData = {
data1 = formData.data1,
data2 = formData.data2
};
try {
setSaving(true);
const { error } = await supabase.from('educations').update(updateData);
if (error) {
seterror(error.message);
}
if (!error) {
navigate('/experiences/education');
}
setSaving(false);
} catch (error: any) {
seterror(error.message);
}
};
return (
<FormProvider {...methods}>
<form className="p-4" onSubmit={methods.handleSubmit(onSubmit)}>
<InputText
id="data1"
label="Data1"
placeholder="Ex: data1"
defaultValue={education?.data1}
options={{ required: 'This field is required' }}
/>
<Button type="submit">{saving ? 'Saving' : 'Save'}</Button>
</form>
</FormProvider>
)
...
myCustomComponent.tsx
import React, { useEffect } from 'react';
import { useFormContext } from 'react-hook-form';
interface InputProps {
id: string;
label: string;
placeholder?: string;
defaultValue?: string;
}
const InputText: React.FC<InputProps> = ({
id,
label,
placeholder,
defaultValue,
options,
...rest
}: InputProps) => {
const {
register,
setValue,
formState: { errors }
} = useFormContext();
useEffect(() => {
if (defaultValue) setValue(id, defaultValue, { shouldDirty: true });
}, [defaultValue, setValue, id]);
return (
<div className="">
<label htmlFor={id} className="">
{label}
</label>
<input
type="text"
placeholder={placeholder}
className=""
id={id}
defaultValue={defaultValue}
{...register(id, options)}
{...rest}
/>
{errors[id] && (<p className="">
<span className="">*</span> {errors[id]?.message}
</p>)}
</div>
);
};
export default InputText;
如您所見,我使用了 formContext 因為我想將我的代碼解構為更小的組件。 現在我有點懷疑我是否正確編碼,特別是當我使用 ut 編輯 forms 時:如果通過“defaultValue”prop 設置我的默認值,我必須提交(錯誤顯示)然后在輸入中 clique 以按順序更改 state清除輸入組件中的錯誤。
這就是為什么我添加了 useEffect 鈎子來清除輸入驗證錯誤並且它正在工作。 你怎么看待這件事? 有沒有更好的方法來管理它(我認為是的,這是設置驗證模式的更簡潔的方法)? 提前致謝,抱歉我生疏的英語。 祝大家度過美好的一天,希望我的代碼能對人們有所幫助。
Edit1 :實際上,我必須兩次提交才能獲取我的數據,所以我想這不是正確的方法,有什么建議嗎?
Edit2 :我找到了一個“解決方案”:如果我的道具中有 defaultValue,我會在我的自定義組件中執行:
useEffect(() => {
if (defaultValue) setValue(id, defaultValue, { shouldDirty: true });
}, [defaultValue, setValue, id]);
我不認為這是更好的解決方案......
Edit3 :謝謝@Jérémy Rippert,這是我的工作解決方案:
editForm.tsx
...
const methods = useForm();
const { reset } = methods;
useEffect(() => {
reset(education);
}, [reset, education]);
return (
<FormProvider {...methods}>
<form className="p-4" onSubmit={methods.handleSubmit(onSubmit)}>
<InputText
id="degree"
label="Degree"
placeholder="Ex: Master 2 - Design & Marketing"
options={{ required: 'This field is required' }}
/>
</form>
</FormProvider>
)
...
myCustomComponent.tsx
...
const InputText: React.FC<InputTextProps> = ({
id,
label,
placeholder,
options
}: InputTextProps) => {
const {
register,
formState: { isDirty, isValid, touchedFields, dirtyFields, errors }
} = useFormContext();
return (
<input
type="text"
placeholder={placeholder}
className={`block w-full rounded-lg border ${
errors[id] ? 'border-red-600' : 'border-gray-600'
} bg-gray-700 p-2.5 text-sm text-white placeholder-gray-400 focus:outline-none`}
id={id}
{...register(id, options)}
/>
)
...
再次感謝@Jérémy Rippert
我錯誤地編輯了我之前的答案,這里是原文:
您應該為 useForm 提供默認值,而不是為您的組件提供默認值(因此您的 InputText 不需要知道 defaultValue 或 setValue,由於 register 方法,它將具有正確的值)。
要初始化表單,您可以這樣做
useForm({ defaultValues: { data1: education?.data1 } });
如果您用於提供默認值的數據在表單初始化后加載,您可以使用 reset 方法(請參閱文檔),我個人將其放入 useEffect 以監視數據更新:
const Component: React.FC = ({ defaultValues }) => {
const {
register,
handleSubmit,
reset,
} = useForm({ defaultValues });
useEffect(() => {
reset(defaultValues);
}, [defaultValues, reset]);
return ...
}
另一方面,您應該在調用它的 useEffect 中而不是在組件中定義 getEducation,這樣就不會在每次呈現組件時都聲明該方法。 片段:
useEffect(() => {
const getEducation = () => {
...
};
getEducation();
}, [educationId]);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.