最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

reactjs - Multiple React-hook-form instances in a parent component resetting data and I cannot figure out how to prevent this -

programmeradmin0浏览0评论

I’m working on a React project where I have multiple instances of react-hook-form in a parent component. Each form is conditionally rendered based on the user’s selection of a tax category (business or individual). However, I’m facing an issue where the form data resets whenever the user switches between categories.

Here's a simplified version of my implementation: |



    import React, { useState } from 'react';
    import { useForm } from 'react-hook-form';

    const ParentComponent = () => {
      const [userSelectedBusinessTaxCategory, setUserSelectedBusinessTaxCategory] = useState(false);
      const [userSelectedIndividualTaxCategory, setUserSelectedIndividualTaxCategory] = useState(false);

      const handleUserSelectedTaxCategory = (category) => () => {
        if (category === 'business') {
          setUserSelectedBusinessTaxCategory((prev) => !prev);
        } else {
          setUserSelectedIndividualTaxCategory((prev) => !prev);
        }
      };

      return (
        <div>
          <button onClick={handleUserSelectedTaxCategory('business')}>Business</button>
          <button onClick={handleUserSelectedTaxCategory('individual')}>Individual</button>

          {userSelectedBusinessTaxCategory && <BusinessForm />}
          {userSelectedIndividualTaxCategory && <IndividualForm />}
        </div>
      );
    };

    const BusinessForm = () => {
      const { register, handleSubmit, reset } = useForm();

      const onSubmit = (data) => {
        console.log(data);
      };

      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('businessName')} placeholder="Business Name" />
          <button type="submit">Submit</button>
        </form>
      );
    };

    const IndividualForm = () => {
      const { register, handleSubmit, reset } = useForm();

      const onSubmit = (data) => {
        console.log(data);
      };

      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('individualName')} placeholder="Individual Name" />
          <button type="submit">Submit</button>
        </form>
      );
    };

    export default ParentComponent;

I’m working on a React project where I have multiple instances of react-hook-form in a parent component. Each form is conditionally rendered based on the user’s selection of a tax category (business or individual). However, I’m facing an issue where the form data resets whenever the user switches between categories.

Here's a simplified version of my implementation: |



    import React, { useState } from 'react';
    import { useForm } from 'react-hook-form';

    const ParentComponent = () => {
      const [userSelectedBusinessTaxCategory, setUserSelectedBusinessTaxCategory] = useState(false);
      const [userSelectedIndividualTaxCategory, setUserSelectedIndividualTaxCategory] = useState(false);

      const handleUserSelectedTaxCategory = (category) => () => {
        if (category === 'business') {
          setUserSelectedBusinessTaxCategory((prev) => !prev);
        } else {
          setUserSelectedIndividualTaxCategory((prev) => !prev);
        }
      };

      return (
        <div>
          <button onClick={handleUserSelectedTaxCategory('business')}>Business</button>
          <button onClick={handleUserSelectedTaxCategory('individual')}>Individual</button>

          {userSelectedBusinessTaxCategory && <BusinessForm />}
          {userSelectedIndividualTaxCategory && <IndividualForm />}
        </div>
      );
    };

    const BusinessForm = () => {
      const { register, handleSubmit, reset } = useForm();

      const onSubmit = (data) => {
        console.log(data);
      };

      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('businessName')} placeholder="Business Name" />
          <button type="submit">Submit</button>
        </form>
      );
    };

    const IndividualForm = () => {
      const { register, handleSubmit, reset } = useForm();

      const onSubmit = (data) => {
        console.log(data);
      };

      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('individualName')} placeholder="Individual Name" />
          <button type="submit">Submit</button>
        </form>
      );
    };

    export default ParentComponent;

The issue arises when handleUserSelectedTaxCategory is called, causing the form to reset. This function toggles the state to determine whether the business or individual form is rendered or removed.

How can I prevent the form data from resetting when switching between categories? Any help or suggestions would be greatly appreciated!

I've tried saving progress to redux and repopulating the Controller (RHF component) code below: tried this in the Controller

  const handleCombinedChangFnsBusiness = (option, question, bizOnchange) => {
    handleChange(question, option.data)
    bizOnchange({
      target: {
        name: `${question.Section}.${question.QuestionID}`,
        value: {
          AnswerID: option.data.answerid,
          AnswerValueLow: option.value.split('.')[1],
          QuestionID: question.QuestionID,
          QuestionText: question.QuestionText,
          Section: question.Section,
          SectionGroup: question.SectionGroup,
          AnswerValueInt: option.data.answerValueINT,
          AnswerText: option.data.answertext,
        },
      },
    })
  }
    const extractQuestionAndAnswerData = (questionID, businessFormData) => {
      console.log('businessFormData is:', businessFormData, "and questionID is:", questionID);
      if (businessFormData.length < 1) {
        return null;
      }

      const selectedFieldData = businessFormData.find(
        (fieldSelection) => fieldSelection.questionID === questionID
      );

      if (selectedFieldData) {
        console.log('selectedFieldData is:', selectedFieldData);
        return {
          label: selectedFieldData.answerText,
          value: `${selectedFieldData.answerId}.${selectedFieldData.answerValueLow}`,
          data: selectedFieldData,
        };
      }

      return null;
    };

      const valueWithProgress = extractQuestionAndAnswerData(question.QuestionID, businessFormData);

    <Controller
      key={`${question.Section}.${question.QuestionID}`}
      name={`${question.Section}.${question.QuestionID}`}
      register={businessRegister}
      rules={{
        required: question.SectionGroup !== 'alacarte',
        validate: (value) => {
          if (value?.AnswerID || question.SectionGroup === 'alacarte') {
            return true
          }
          return 'This field is required'
        },
      }}
      control={businessControl}
      // prevent resetting of form data when another form is rendered
      defaultValue={
        businessFormData.length > 0
          ? {
              AnswerID: valueWithProgress?.data?.answerId,
              AnswerValueLow: valueWithProgress?.data?.answerValueLow,
              QuestionID: question.QuestionID,
              QuestionText: question.QuestionText,
              Section: question.Section,
              SectionGroup: question.SectionGroup,
              AnswerValueInt: valueWithProgress?.data?.answerValueInt,
              AnswerText: valueWithProgress?.data?.answerText,
            }
          : businessFormDataRef.current[`${question.QuestionID}`]
      }
      render={({
        field: { onChange: businessOnChange, value },
        fieldState: { error: businessFieldError },
      }) => (
        <div className="d-flex flex-row col-12 justify-content-between align-items-center">
          <div className="d-flex flex-column col-6 ms-2">
            {question.SectionGroup === 'alacarte' && !value?.AnswerID && (
              <span className="text-primary fw-semibold fs-6 position-relative top-0 right-100">
                *optional*
              </span>
            )}
            <CFormFloating
              className="mb-3 shadow-sm bg-body rounded pl-10 position-relative top-0 left-100 w-100 justify-content-end align-items-start rounded-3 shadow bg-body-primary border-0 rounded-0"
              style={{
                minWidth: 700,
                padding: 10,
                fontSize: '.85rem'
              }}
            >
              {' '}
              {question.SectionGroup !== 'main' ? (
                <h4
                  component="h4"
                  id={`${question.Section}.${question.QuestionID}-help`}
                  className="fw-bold text-primary mb-2"
                  style={{ fontSize: '.80rem' }}
                >
                  {question.QuestionText}
                </h4>
              ) : (
                question.QuestionText
              )}
              {question.SectionGroup !== 'alacarte' && !value?.AnswerID && (
                <span className="text-danger fw-light fs-6 position-absolute bottom-0 start-100  me-2 mb-4">
                  *required*
                </span>
              )}
              <Select
                isSearchable={false}
                styles={selectStyles}
                aria-label={question.QuestionText}
                role="combobox"
                isDisabled={businessValues?.entityName === ''}
                aria-required="true"
                aria-invalid={!!businessFieldError}
                className="form-select-lg w-75 mb-3 justify-content-start align-items-start rounded-3 shadow bg-body-primary border-0 rounded-0"
                onChange={(option) =>
                  handleCombinedChangFnsBusiness(option, question, businessOnChange)
                }
                value={
                  businessFormDataRef.current
                    ? businessFormDataRef.current[`${question.QuestionID}`]?.AnswerID
                    : null
                }
                options={question.Answers?.filter((answerOption) => answerOption.IsActive).map(
                  (answerOption) => ({
                    label: answerOption.AnswerText,
                    value: `${answerOption.AnswerID}.${answerOption.AnswerValueLow}`,
                    data: {
                      answerid: answerOption.AnswerID,
                      answertext: answerOption.AnswerText,
                      answerValueINT: answerOption.AnswerValueLow,
                    },
                  }),
                )}
                getOptionLabel={(option) => option.label}
                getOptionValue={(option) => option.value}
                type="select"
                defaultValue="please select an option"
                ref={ref}
                name={`${question.Section}.${question.QuestionID}`}
                id={`${question.Section}.${question.QuestionID}`}
                aria-describedby={`${question.Section}.${question.QuestionID}-help`}
                required
              />
            </CFormFloating>
            {businessFieldError && (
              <div className="text-danger fw-light fs-6 position-absolute top-0 right-100">
                {businessFieldError.message}
              </div>
            )}
          </div>


     
          
    />

The code below is the hook that saves form progress to redux.



    const useFormDataBusinessProgress = () => {
      const dispatch = useDispatch();
      const [businessFormData, setBusinessFormData] = useState({});

      const handleBusinessFormChange = (question, answer) => {
        const uniqueId =
          question.sectionGroup === 'alacarte' ? uuidv4() : question.QuestionID;
        const isAlacarteItem = question.sectionGroup === 'alacarte';

        const updatedFormData = {
          ...businessFormData,
          [answer.answerid]: {
            answerId: answer.answerid,
            answerText: answer.answertext,
            answerValueLow: answer.answerValueINT,
            answerValueHigh: null,
            section: question.Section,
            sectionGroup: question.SectionGroup,
            questionText: question.QuestionText,
            questionDescription: question.QuestionDescription,
            questionID: question.QuestionID,
            isActive: question.IsActive,
            required: question.Required,
            services: question.Services,
            displayOrder: question.DisplayOrder,
            Answers: question.Answers,
          },
        };

        if (isAlacarteItem) {
          const formProgress = businessFormData.formProgress
            ? [...businessFormData.formProgress, updatedFormData]
            : [updatedFormData];
          setBusinessFormData((prevState) => ({
            ...prevState,
            formProgress,
          }));
        } else {
          setBusinessFormData(updatedFormData);
        }

        dispatch({
          type: 'SAVE_BUSINESS_FORM_DATA_PROGRESS',
          payload: isAlacarteItem ? formProgress : updatedFormData,
        });
      };

      return [businessFormData, handleBusinessFormChange];
    };

and although the data is saving to redux and properly able to extract all the data we need to populate the form, just CANNOT get the UI inputs to repopulate based on the redux formProgress data

any and all help will be greatly appreciated I'm not sure if I made a fatal design mistake or what. Thanks in advance and have a great day!

Share Improve this question asked Feb 6 at 13:21 Robert ZobristRobert Zobrist 5911 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

You can try using useForm hooks directly in the parent and passing them as props to the child components.

import React, { useState } from 'react';
import { useForm } from 'react-hook-form';

const ParentComponent = () => {
  const [userSelectedBusinessTaxCategory, setUserSelectedBusinessTaxCategory] = useState(false);
  const [userSelectedIndividualTaxCategory, setUserSelectedIndividualTaxCategory] = useState(false);

  const businessForm = useForm();
  const individualForm = useForm();

  const handleUserSelectedTaxCategory = (category) => () => {
    if (category === 'business') {
      setUserSelectedBusinessTaxCategory((prev) => !prev);
    } else {
      setUserSelectedIndividualTaxCategory((prev) => !prev);
    }
  };

  return (
    <div>
      <button onClick={handleUserSelectedTaxCategory('business')}>Business</button>
      <button onClick={handleUserSelectedTaxCategory('individual')}>Individual</button>

      {userSelectedBusinessTaxCategory && <BusinessForm form={businessForm} />}
      {userSelectedIndividualTaxCategory && <IndividualForm form={individualForm} />}
    </div>
  );
};

const BusinessForm = ({ form }) => {
  const { register, handleSubmit, reset } = form;

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('businessName')} placeholder="Business Name" />
      <button type="submit">Submit</button>
    </form>
  );
};

const IndividualForm = ({ form }) => {
  const { register, handleSubmit, reset } = form;

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('individualName')} placeholder="Individual Name" />
      <button type="submit">Submit</button>
    </form>
  );
};

export default ParentComponent;

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论