-1

Edited

I am trying to make a form that will enable the user to edit an existing citation. The citation is pulled from the backend with an api call. I then assign the values grabbed from the api to the state variables. These state variables is passed as props to the form jsx where they are used in the value prop of the input field. But for some reason the values assigned to state is not retained by the state and it goes back to its initial state values. I am adding the code as to how the component looks like

const EditEnterCitation = () => {
  const [searchParams, setSearchParams] = useSearchParams();

  // getting params from the url to pass to the functions
  const cat = searchParams.get("cat");
  const { id } = useParams();

  // state to pass to the value prop in the form input
  const [formData, setFormData] = useState({
    institution_name: "0",
    judge_name: "",
    case_no: "",
    apelLate_type: "0",
    law: "",
    point_of_law: "0",
    party_name_appelant: "",
    party_name_respondent: "",
    advocate_petitioner: "",
    advocate_respondent: "",
    judgements: "",
    date_of_order: "",
    headnote: "",
    references: "",
    equivalent_citations: "",
    title: "",
  });
 
  // state to for select buttonn in the form
  const [instName, setInstName] = useState([]);

  const [lawChoiceOptions, setlawChoiceOptions] = useState([]);

  const [pointOfLawOptions, setPointOfLawOptions] = useState([]);

  const [appealateType, setAppealateType] = useState([]);

  // getting the choices for select from backend 
  const allChoices = useQuery(
    ["allChoices", cat],
    () => getAllChoices(cat),

    {
      enabled: cat !== null,
      refetchOnWindowFocus: false,
      onSuccess: (response) => {
        // setting law choices
        if (response.data.law) {
          let arr = response.data.law.map((element, index) => {
            return { value: element.law_name, label: element.law_name };
          });
          setlawChoiceOptions(arr);
        }
        // setting point of law options
        setPointOfLawOptions(response.data.pol);

        // setting appealate type
        setAppealateType(response.data.appeal);

        // setting ins choices
        if (response.data.ins.length) {
          setInstName(response.data.ins);
        } else {
          let court_names = [];

          let tribunal_name = [];

          if (response.data.ins.ins_court.length > 0) {
            court_names = response.data.ins.ins_court;
          }

          if (response.data.ins.ins_tribunal.length > 0) {
            tribunal_name = response.data.ins.ins_tribunal;
          }

          let ins_names = court_names.concat(tribunal_name);

          setInstName(ins_names);
        }
      },
    }
  );

  // function to get default values of the citation
  const getDefaultValues = useQuery(
    ["detailCitation", id],
    () => detailCitation(cat, id),
    {
      enabled: allChoices.isFetched === true,
      refetchOnWindowFocus: false,
      onSuccess: (response) => {
        setFormData({
          institution_name: response.data.institution_name,
          judge_name: response.data.judge_name,
          case_no: response.data.case_no,
          apelLate_type: response.data.apelLate_type,
          law: response.data.law,
          point_of_law: response.data.point_of_law,
          party_name_appelant: response.data.party_name_appelant,
          party_name_respondent: response.data.party_name_respondent,
          advocate_petitioner: response.data.advocate_petitioner,
          advocate_respondent: response.data.advocate_respondent,
          judgements: response.data.judgements,
          date_of_order: response.data.date_of_order,
          headnote: response.data.headnote,
          references: response.data.references,
          equivalent_citations: response.data.equivalent_citations,
          title: response.data.title,
        });
      },
    }
  );

  // handling form data change for some fields
  const handleFormDataChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  // handling form data change for the judgement
  const handleJudgementData = (data) => {
    setFormData({ ...formData, judgements: data });
  };

  const handlePartyAppealData = (data) => {
    setFormData({ ...formData, party_name_appelant: data });
  };

  const handlePartyRespondData = (data) => {
    setFormData({ ...formData, party_name_respondent: data });
  };

  // handling data change of the law choices
  const handleOnChangeLaw = (selectedOption) => {
    let newArray = [];

    selectedOption.map((element) => {
      newArray.push(element.value);
    });

    setFormData({ ...formData, law: newArray.toString() });
  };

  // hadling sumbimission of citation
  const handleFormDataSubmit = async (e) => {
    e.preventDefault();

    try {
      setProgress(80);
      let response = await addCitation(cat, formData);
      if (response.status === 201) {
        toastNotification(
          `Citation Uploaded to ${cat.toUpperCase()}`,
          `success`
        );
        setProgress(100);
        goToTop();
      } else {
        toastNotification(`Server Error. Could not upload citation`, `error`);
        setProgress(100);
      }
    } catch (error) {}
  };

  useEffect(() => {
    console.count("formData Appearing");
    console.log(formData);
  });

  return (
    <>
      <LoadingBar
        color="red"
        progress={progress}
        onLoaderFinished={() => setProgress(0)}
        height={4}
      />
      <Helmet>
        <title>Enter Citation</title>
      </Helmet>
      <Wrapper>
        <FormContainer onSubmit={handleFormDataSubmit}>
          <EditInsFormElements
            cat={cat}
            handleFormDataChange={handleFormDataChange}
            formData={formData}
            handleJudgementData={handleJudgementData}
            handleOnChangeLaw={handleOnChangeLaw}
            instName={instName}
          />
          <EditOtherFormElements
            cat={cat}
            handleFormDataChange={handleFormDataChange}
            formData={formData}
            handleJudgementData={handleJudgementData}
            handleOnChangeLaw={handleOnChangeLaw}
            lawChoiceOptions={lawChoiceOptions}
            pointOfLawOptions={pointOfLawOptions}
            appealateType={appealateType}
            handlePartyAppealData={handlePartyAppealData}
            handlePartyRespondData={handlePartyRespondData}
          />

          <FormFooter>
            <UploadBtn disabled={disableSubmit()} type="submit">
              Upload Citation
            </UploadBtn>
          </FormFooter>
        </FormContainer>
      </Wrapper>
    </>
  );
};

the jsx inside the EditInsFormElements and EditOtherFormElements looks like this

 <label className="required-field" htmlFor="apelLate_type">
          Apellate Type*
        </label>
        <select
          name="apelLate_type"
          id="apelLate_type"
          value={formData.apelLate_type}
          onChange={handleFormDataChange}
          required
        >
          <option value="0" disabled hidden>
            Select
          </option>
          {appealateType &&
            appealateType.length &&
            appealateType.map((element, index) => {
              return (
                <option key={index} value={element.appealate_type}>
                  {element.appealate_type}
                </option>
              );
            })}
        </select>
 <label className="required-field" htmlFor="case_no">
          Case No*
        </label>
        <TextArea
          name="case_no"
          id="case_no"
          value={formData.case_no}
          onChange={handleFormDataChange}
          required
        />
  <label className="required-field" htmlFor="title">
          Title*
        </label>
        <input
          type="text"
          name="title"
          value={formData.title}
          onChange={handleFormDataChange}
          required
        /> ....

And the state behaviour in the console that I have produced using useEffect is attached below

enter image description here

Question Why is this alternating behaviour seen in the console is caused and how to rectify it so that the state retains the values grabbed and assigned from the backend.

  • There's not enough information to debug here... Where in your code are your `console.log()` lines? What does `EditInsFormElements` and `EditOtherFormElements` do with `formData`? What does `handleFormDataChange` look like? – Phil Feb 14 '23 at 05:04
  • @Phil I have added the codes that you wanted and handleformDataChange is just an onchange function used for forms. But the concern here is I am not able to assign data to the state variables. – Ritankar Bhattacharjee Feb 14 '23 at 05:21
  • What makes you think `handleFormDataChange` isn't necessary to answering this question? It's highly likely you're using `setFormData()` incorrectly – Phil Feb 14 '23 at 05:27
  • @Phil Perhaps my question is unclear to you. What I am saying here is I want to prefill the forms with data that I have grabbed from the api. I cannot prefill it because of the wierd behaviour shown in console. The data is not retained by the state. FormDataChange is not in picture here at all. – Ritankar Bhattacharjee Feb 14 '23 at 05:30
  • Without a [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example), this is impossible to answer. Also, what version of React Query are you using? – Phil Feb 14 '23 at 05:32
  • Also, where do `cat` and `id` come from? Is `id` state? Does it change at all? – Phil Feb 14 '23 at 05:35
  • @Phil ```cat``` and ```id``` are params that come from the url which I pass in the function to get data. – Ritankar Bhattacharjee Feb 14 '23 at 05:37
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/251851/discussion-between-ritankar-bhattacharjee-and-phil). – Ritankar Bhattacharjee Feb 14 '23 at 05:43
  • No, I've already asked you to [edit] your question to provide an MRE. As it stands, this is not reproducible but I had to guess what some of your implementation looked like ~ https://codesandbox.io/s/determined-burnell-egkirb?file=/src/App.js – Phil Feb 14 '23 at 05:49
  • @Phil correct you have guessed it right. Now here what happens when you set the institution name inside the react query the value is retained right? and the iput has a default value. But in my component it is not retained. That is what I was asking as to why is this happening – Ritankar Bhattacharjee Feb 14 '23 at 05:56
  • @Phil I have added all the details that might help you to debug the state behaviour aand edited the complete question – Ritankar Bhattacharjee Feb 14 '23 at 06:13
  • You really need to pare this back to the absolute **minimal** reproduction. Start with a blank slate and gradually add parts until the problem resurfaces; remove any editing capabilities, remove the reliance on the `allChoices` query – Phil Feb 14 '23 at 06:24
  • This is probably solved by this post ~ [Why React useState with functional update form is needed?](https://stackoverflow.com/q/57828368/283366). But that would just be a bandaid on top of what is probably a bad call to `handleFormDataChange()` – Phil Feb 14 '23 at 06:33
  • @ I started adding one input at a time and observed that when I use the ckeditor as input field I am starting to have this problem. – Ritankar Bhattacharjee Feb 14 '23 at 06:59
  • This is practically the same as [this question](https://stackoverflow.com/q/75442200/283366) then and is solved by the same duplicate I linked above. – Phil Feb 14 '23 at 08:33
  • @Phil yes phil something similar was happening with me as well and lokking at that answer I figured it out, thanks – Ritankar Bhattacharjee Feb 14 '23 at 12:35

1 Answers1

0

So after debugging I founf out that the problem was with the CkEditor component I was using, whose onChange function was creating this behaviour.

the way I was using the onChange function on one of the ckeditor component was like below

 <CKEditor
          editor={Editor}
          data={formData.judgements}
          name="judgements"
          id="judgements"
          onChange={(event, editor, e) => {
          const data = editor.getData()
          handleJudgementsData(data)
} }
        />

and the onChange function looked like this

 const handleJudgementData = (data) => {
    setFormData({ ...formData, judgements: data });
  };

This was causing the problem as the state was not updating synchronously what I could understand from Phil's answer in the question he linked

so I changed the code in the below way and now it is working

jsx

      <CKEditor
          editor={Editor}
          data={formData.judgements}
          onChange={(event, editor, e) => handleJudgementData(event, editor, e)}
        />

onChnage function

const handleJudgementData = (event, editor) => {
    let data = editor.getData();
    setFormData((prev) => ({ ...prev, judgements: data }));
  };

After doing it the above way now the state retains the data fetched from api