无法分配从 useState 中的 api 检索到的值

Cannot assign value retrieved from api inside useState

我想在输入字段的值发生变化时更新 firmDetail 的值。为此,我正在从 API 获取数据并尝试将从 API 检索到的值分配给 useState 中的 firmDetail。但是由于从 API 中检索数据的延迟导致 firmProfile 中出现空值问题。这是我的代码:

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>

firmDetail 不能是无条件调用 setFirmDetail 的效果的依赖项,因为这将创建渲染循环。我怀疑您打算使用来自 UseApiFetch 挂钩的 firmProfile 数据响应。

useEffect 挂钩将在初始渲染周期中 运行,很可能在 GET 请求解析之前。您应该在 尝试访问嵌套属性之前通过检查它是否存在 来考虑可能为空的 firmProfile 值。只有当它是 truthy/defined 时,你才应该实际排队状态更新。

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]);

从 JSX 中的 firmDetail 状态渲染

{!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>
        ...