Catalin Mares
Catalin Mares

Reputation: 105

Update React input fields values on demand

I am trying to implement a two-page form in React. On the first page I request a code that I use to fetch some data that I need to display on the second page. Here, by page I mean a different view on the same page. The problem that I am facing is that when I receive a response from the backend API, I use state to update the current state of the fields and this does not seem to happen immediately when I need it. When I change the value of the property that decides which view to render, the fields are still empty, not updating with the values fetched from the API. Now, I know that setState is asynchronous, but I don't know how to handle this situation, since the data fetch is done on demand, not at the beginning, so I don't think I can use useEffect for this job.

Here is my state:

  const [state, setState] = useState({
    name: "",
    cif: "",
    address: "",
    phone: "",
    fax: "",
    managerName: "",
    email: "",
    password: "",
  });
  const [companyDataFetched, setCompanyDataFetched] = useState(false);

And here are the two views and how I switch between them:

{!companyDataFetched ? (
              <CustomForm layout={theme.layout} widthelem={"70%"}>
                <Form.Item
                  name="Company CIF:"
                  label="Company CIF:"
                  rules={[
                    {
                      pattern: new RegExp(/^\d{2,10}$/),
                      message: "Invalid CIF!",
                    },
                    {
                      required: true,
                      message: "Please insert the CIF of the company!",
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "cif")}
                  />
                </Form.Item>
                <CustomButton
                  backgroundcolor={theme.primaryColor}
                  textcolor={theme.white}
                  onClick={() => {
                    axios.get(`${process.env.REACT_APP_API_URL}/Company/GetByCUI/${state.cif}`)
                      .then((res) => {
                        const companyInfo = res.data;

                        if (companyInfo.name) {
                          setState((prevState) => {
                            return {
                              ...prevState,
                              name: companyInfo.name
                            };
                          });
                        }

                        if (companyInfo.address) {
                          setState((prevState) => {
                            return {
                              ...prevState,
                              "address": companyInfo.address,
                            };
                          });
                        }

                        if (companyInfo.phone) {
                          setState((prevState) => {
                            return {
                              ...prevState,
                              "phone": companyInfo.phone,
                            };
                          });
                        }

                        if (companyInfo.fax) {
                          setState((prevState) => {
                            return {
                              ...prevState,
                              "fax": companyInfo.fax,
                            };
                          });
                        }

                        setCompanyDataFetched(true);
                      })
                      .catch((error) => {
                        console.log(error);
                      });
                  }}
                  margintop={"13%"}
                  marginbottom={"13%"}
                >
                  Continue
                </CustomButton>
              </CustomForm>
            ) : (
              <CustomForm layout={theme.layout} widthelem={"70%"}>
                <Form.Item
                  name="Company name:"
                  label="Company name:"
                  rules={[
                    {
                      required: true,
                      message: "Please insert the name of the company!",
                      whitespace: true,
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "name")}
                    value={state.name}
                  />
                </Form.Item>
                <Form.Item
                  name="Company CIF:"
                  label="Company CIF:"
                  rules={[
                    {
                      pattern: new RegExp(/^\d{2,10}$/),
                      message: "Invalid CIF!",
                    },
                    {
                      required: true,
                      message: "Please insert the CIF of the company!",
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "cif")}
                    value={state.cif}
                    disabled={true}
                  />
                </Form.Item>
                <Form.Item
                  name="Address:"
                  label="Address:"
                  rules={[
                    {
                      required: true,
                      message: "Please insert the address of the company!",
                      whitespace: true,
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "address")}
                    value={state.address}
                  />
                </Form.Item>
                <Form.Item
                  name="Phone:"
                  label="Phone:"
                  rules={[
                    {
                      required: true,
                      message:
                        "Please insert the phone number of the company!",
                      whitespace: true,
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "phone")}
                    value={state.phone}
                  />
                </Form.Item>
                <Form.Item
                  name="Fax:"
                  label="Fax:"
                  rules={[
                    {
                      required: true,
                      message: "Please insert the fax of the company!",
                      whitespace: true,
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "fax")}
                    value={state.fax}
                  />
                </Form.Item>
                <Form.Item
                  name="Manager name:"
                  label="Manager name:"
                  rules={[
                    {
                      required: true,
                      message:
                        "Please, insert the name of the company manager!",
                      whitespace: true,
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) =>
                      handleChange(event, "managerName")
                    }
                  />
                </Form.Item>
                <Form.Item
                  name="Manager e-mail:"
                  label="Manager e-mail:"
                  rules={[
                    {
                      type: "email",
                      message: "Invalid e-mail!",
                    },
                    {
                      required: true,
                      message:
                        "Please insert the e-mail of the company manager!",
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "email")}
                  />
                </Form.Item>
                <Form.Item
                  name="Password:"
                  label="Password:"
                  rules={[
                    {
                      required: true,
                      message: "Please, insert a password!",
                    },
                    {
                      min: 4,
                      message: "Password needs to be at least 4 characters long",
                    },
                  ]}
                >
                  <CustomInput
                    backgroundcolor={theme.white}
                    onChange={(event) => handleChange(event, "password")}
                    type={"password"}
                    onKeyPress={verifyCredentials}
                  />
                </Form.Item>
                <CustomButton
                  backgroundcolor={theme.primaryColor}
                  textcolor={theme.white}
                  onClick={() => {
                    signup();
                  }}
                  margintop={"13%"}
                  marginbottom={"13%"}
                >
                  Register
                </CustomButton>
              </CustomForm>
            )}

The major problem is that when companyDataFetched becomes true, the state containing the field values is not updated and the values of the fields don't update with the data fetched from the backend. How can I handle this issue?

[UPDATE AFTER PAIMAN'S SUGGESTION]

I tried using useEffect and another state for the API response, but the result is the same.

Here is my updated code:

  useEffect(() => {
    if (companyDataFetched) {
      console.log(response);

      setState((prevState) => {
        return {
          ...prevState,
          nume: response.name,
          adresa: response.address,
          telefon: response.phone,
          fax: response.fax
        }
      });
    }
  }, [companyDataFetched, response]);
axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
     .then((res) => {
       setResponse(res.data);
       setCompanyDataFetched(true);
     })
     .catch((error) => {
       console.log(error);
     });

[UPDATE AFTER SANGEET'S SUGGESTION]

I tried using what you suggested. It did not work straight-forward, so I made some changes, but it doesn't work yet.

Here is my updated code:

useEffect(() => {
    const fetchData = () => {
        return axios
           .get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
           .then((res) => {
               return res;
           })
           .catch((error) => {
               console.log(error);
           });
    };

    let isMounted = true;
 
    if (companyDataFetched) {
        (async () => {
            const response = await fetchData();

            if (isMounted) {
                setState((prevState) => {
                    return {
                        ...prevState,
                        name: response.data.name,
                        address: response.data.address,
                        phone: response.data.phone,
                        fax: response.data.fax,
                    };
                });
            } else {
                setCompanyDataFetched(false);
            }
        })();
    }
  
    return function () {
        isMounted = false;
    };
}, [companyDataFetched, state.cif]);

Upvotes: 1

Views: 850

Answers (3)

Catalin Mares
Catalin Mares

Reputation: 105

After a lot of research, I discovered that I was setting the input fields values in a wrong way. Apparently, when using antd Form, it must be initialized and used its reference to dynamically change Form.Item values, like so:

const [form] = Form.useForm();
const [step, setStep] = useState(1);
const [state, setState] = useState({
  name: "",
  cif: "",
  address: "",
  phone: "",
  fax: "",
  contactName: "",
  email: "",
  password: "",
});

const fetchCompanyData = async () => {
  const response = await axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`);

    setState((prevState) => {
      return {
        ...prevState,
        nume: response.data.denumire,
        adresa: response.data.adresa,
        telefon: response.data.telefon,
        fax: response.data.fax,
      };
    });

    form.setFieldsValue({
      nume: response.data.denumire,
      cif: state.cif,
      adresa: response.data.adresa,
      telefon: response.data.telefon,
      fax: response.data.fax,
    });

    setStep(2);
  };
};

[...]

{step === 1 && (
<CustomForm form={form} layout={theme.layout} widthelem={"70%"}>
  <Form.Item
    name="Company name:"
    label="Company name:"
    rules={[
      {
        pattern: new RegExp(/^\d{2,10}$/),
        message: "Invalid CIF!",
      },
      {
        required: true,
        message: "Please insert the company CIF!",
      },
    ]}
  >
    <CustomInput
      backgroundcolor={theme.white}
      onChange={(event) => handleChange(event, "cif")}
    />
  </Form.Item>
  <CustomButton
    backgroundcolor={theme.primaryColor}
    textcolor={theme.white}
    onClick={fetchCompanyData}
    margintop={"13%"}
    marginbottom={"13%"}
  >
    Continue
  </CustomButton>
<CustomForm>
)}
{step === 2 && (
<CustomForm form={form} layout={theme.layout} widthelem={"70%"}>
  <Form.Item
    name="Company name:"
    label="Company name:"
    rules={[
      {
        required: true,
        message: "Please insert the name of the company!",
        whitespace: true,
      },
    ]}
  >
    <CustomInput
      backgroundcolor={theme.white}
      onChange={(event) => handleChange(event, "name")}
    />
  </Form.Item>
  [...]
<CustomForm>
)}

Upvotes: 0

Paiman Rasoli
Paiman Rasoli

Reputation: 1214

[UPDATED!] multi-step form implementation. pretend you get the phone number in step 1 then email in step 2.

// component.js
export default function MultiForm(){
 const [step , setStep] = useState(1);
 const [inputs , setInputs] = useState({
    phone : '',
    email : ''
 });
const handleStep1 = () => {
// validation
 if(inputs.phone !== ""){
    // set step 2
     setStep(2)
  }
}
}
     const handleStep2 = async() => {
// validation
 if(inputs.email !== ""){
    const { data , status } = await axios.post(url ,{
       phone : inputs.phone,
       email : inputs.email
     })
     if(status === 200){
         alert('Registered successfully')
      }
  }
}
}
 return (
    <>
     {step === 1 && (
            <>
        <input name="phone" onChange={(e) =>{
            setInputs({...inputs , [e.target.name] : e.target.value})
        }} />
      <button onClick={handleStep1}>Continue</button>
      </>
    )}
   {step === 2 && (
             <input name="email" onChange={(e) =>{
            setInputs({...inputs , [e.target.name] : e.target.value})
        }} />
      <button onClick={handleStep2}>Submit</button>

   )}

    </>
 )
  }

Upvotes: 1

Sangeet Agarwal
Sangeet Agarwal

Reputation: 1825

Don't have your API call in the onClick handler. Instead trigger the fetch in the button by saying setCompanyDataFetch(true).

Then have a useEffect with companyDataFetch as a dependency.

function fetchData() {  
   return axios.get(`${process.env.REACT_APP_INTERNSHIP_API_URL}/Company/GetByCUI/${state.cif}`)
         .then((res) => {
           // setResponse(res.data);
           // setCompanyDataFetched(true);
           return res;
         })
         .catch((error) => {
           console.log(error);
         });
}

Your useEffect looks like so

  useEffect(() => {
    let isMounted = true;
    if (companyDataFetched) {
      (async () => {
       const response = await fetchData() 
       if (isMounted) setState((prevState) => {
                        return {
                          ...prevState,
                          nume: response.name,
                          adresa: response.address,
                          telefon: response.phone,
                          fax: response.fax
                        }
                      });   
      if (isMounted) setCompanyDataFetched(false);      
     })();
    }
    return function () {
      isMounted = false;
    };
  }, [companyDataFetched]); 

Upvotes: 1

Related Questions