簡體   English   中英

使用 react-hook-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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM