Sujan
Sujan

Reputation: 157

Cannot assign value retrieved from api inside useState

I want to update the value of firmDetail when there is a change in the value of input fields. To do so, I am fetching data from API and trying to assign the retrieved value from API to firmDetail in useState. But due to delays in retrieving data from API causing issues of null value in firmProfile. Here is my code:

const UseApiFetch = (url) => {
    const[data, setData]= useState(null);
    const[isPending, setPending]= useState(true);
    const[error, setError]= useState(null);

    useEffect( async ()=>{
        await axios.get(url).then(
            response =>{
                if(!response.status=== "200"){
                    throw error('error while fetching data');
                }
                setData(response.data);
                setPending(false);
                console.log(response.data);
            })
            .catch((error => {
                setPending(false);
                setError(error.message);
            })
            )
    }, [url]);
    return{data, isPending, error}
}

const { data: firmProfile, isPending, error } = UseApiFetch(constants.FIRM_PROFILE_URI);
const[firmDetail, setFirmDetail]= useState({
    firmName:"",
    firmEmail:"",
    firmPhone:"",
    firmUrl:"",
    pDate:"",
    cRename:""
})
useEffect(()=>{
    setFirmDetail({
        ...firmDetail,
        firmName: firmProfile.firmSubscriptionDetail.firmName,
        firmEmail: firmProfile.firmSubscriptionDetail.ltbDecisionEmail,
        firmPhone: firmProfile.firmSubscriptionDetail.ltbDecisionPhone,
        firmUrl: firmProfile.firmSubscriptionDetail.firmURL,
        pDate: firmProfile.firmSubscriptionDetail.purchaseDate,
        cRename: firmProfile.firmSubscriptionDetail.caseRename
    })
}, [firmDetail])
// if(firmProfile){
//     firmDetail.firmName = firmProfile.firmSubscriptionDetail.firmName;
//     firmDetail.firmEmail=firmProfile.firmSubscriptionDetail.ltbDecisionEmail;
//     firmDetail.firmPhone=firmProfile.firmSubscriptionDetail.ltbDecisionPhone;
//     firmDetail.firmUrl=firmProfile.firmSubscriptionDetail.firmURL;
//     firmDetail.pDate=firmProfile.firmSubscriptionDetail.purchaseDate;
//     firmDetail.cRename=firmProfile.firmSubscriptionDetail.caseRename;
// }
function handleChange(evt) {
    const value = evt.target.value;
    setFirmDetail({
        ...firmDetail,
        [evt.target.name]: value
    });
}
return (
    <div className="content">
        {error && <div>{error}</div>}
        {isPending &&
            <div><Loader /></div>
        }
        <div className="firmDetail">
        { firmProfile &&
            <div className="col-lg-6">
                <CCard>
                    <CCardHeader>
                        Firm
                        <small> Profile</small>
                    </CCardHeader>
                    <CCardBody>
                        <CFormGroup>
                            <CLabel htmlFor="name">Firm Name:</CLabel>
                            <CInput id="firmName" name="firmName" defaultValue={firmProfile.firmSubscriptionDetail.firmName} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <CFormGroup>
                            <CLabel htmlFor="email">Contact Email:</CLabel>
                            <CInput id="firmEmail" name="firmEmail" type="email" defaultValue={firmProfile.firmSubscriptionDetail.ltbDecisionEmail} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <CFormGroup>
                            <CLabel htmlFor="phone">Contact Phone:</CLabel>
                            <CInput id="firmPhone" name="firmPhone"  defaultValue={firmProfile.firmSubscriptionDetail.ltbDecisionPhone} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <CFormGroup>
                            <CLabel htmlFor="">Firm Url:</CLabel>
                            <CInput id="firmUrl" name="firmUrl" defaultValue={firmProfile.firmSubscriptionDetail.firmURL} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <CFormGroup>
                            <CLabel htmlFor="date">Purchase Date:</CLabel>
                            <CInput id="date" name="pDate" defaultValue={firmProfile.firmSubscriptionDetail.purchaseDate} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <CFormGroup>
                            <CLabel htmlFor="case">Case Rename</CLabel>
                            <CInput id="caseRename" name="cRename" defaultValue={firmProfile.firmSubscriptionDetail.caseRename} onChange={(e)=>handleChange(e)}/>
                        </CFormGroup>
                        <Row className="float-right">
                            <Button variant="success pull-right" className="float-right">Update</Button>
                        </Row>
                    </CCardBody>
                </CCard>
            </div>
        }
    </div>

Upvotes: 2

Views: 122

Answers (1)

Drew Reese
Drew Reese

Reputation: 202605

firmDetail can't be a dependency for the effect that unconditionally calls setFirmDetail as this will create a render loop. I suspect you meant to use the firmProfile data response from the UseApiFetch hook.

The useEffect hook will run on the initial render cycle, likely well before the GET request resolves. You should account for the possibly null firmProfile value by checking if it exists before attempting to access nested properties. Only if it is truthy/defined should you actually enqueue a state update.

useEffect(()=>{
  firmProfile && setFirmDetail({
    firmName: firmProfile.firmSubscriptionDetail.firmName,
    firmEmail: firmProfile.firmSubscriptionDetail.ltbDecisionEmail,
    firmPhone: firmProfile.firmSubscriptionDetail.ltbDecisionPhone,
    firmUrl: firmProfile.firmSubscriptionDetail.firmURL,
    pDate: firmProfile.firmSubscriptionDetail.purchaseDate,
    cRename: firmProfile.firmSubscriptionDetail.caseRename,
  })
}, [firmProfile]);

Render from the firmDetail state in your JSX

{!isPending &&
  <div className="col-lg-6">
    <CCard>
      <CCardHeader>
        Firm
        <small> Profile</small>
      </CCardHeader>
      <CCardBody>
        <CFormGroup>
          <CLabel htmlFor="name">Firm Name:</CLabel>
          <CInput
            id="firmName"
            name="firmName"
            value={firmDetail.firmName} // <-- controlled input
            onChange={handleChange}
          />
        </CFormGroup>
        ...

Upvotes: 1

Related Questions