我有一个React组件,它从一些数据中计算价格,并以道具形式接收。价格是根据componentDidUpdate
计算出来的,该价格会触发带有输入数据的网络请求。我想对网络请求进行反跳,以免淹没我的api。
所以我利用lodash.debounce
并在构造函数中初始化。但是在第一个网络请求中,响应为undefined
。以下请求已定义响应。在我的网络标签中,我看到响应不是undefined
。而且,如果我不对请求进行退信处理(即直接用api.calculateContractOffer(offerData)
调用api,那么它也不是undefined
。
因此,在取消网络请求时,我一定做错了。但是有什么错误呢?
我尝试了this answer,但是它没有使用异步的网络请求。所以我怀疑这是问题所在...如果我是对的,那么在反跳时如何处理异步代码?
import React from 'react'
import * as api from 'api/api'
import debounce from 'lodash.debounce'
interface IOwnProps {
visible: boolean
freeContract: boolean
vehicle: Vehicle | undefined
contractTemplateId: number | undefined
duration: number | undefined
mileage: number | undefined
optionIds: number[]
providerPayments: number
providerShare: number
onCalculationChange: (priceCalc: ICalculationResponse | undefined) => void
}
interface IState {
error: string
offerPrice: ICalculationResponse | undefined
}
class ContractOfferBreakdown extends React.Component<TProps, IState> {
public state: IState = {
error: '',
offerPrice: undefined,
}
public constructor(props: TProps) {
super(props)
this.calculateContractOfferDebounced = debounce(this.calculateContractOfferDebounced, 200)
}
public async componentDidUpdate(prevProps: TProps) {
const { contractTemplateId, optionIds, duration, mileage, freeContract, onCalculationChange } = this.props
// data necessary for price calculation is missing
const dataIsMissing = !contractTemplateId || !duration || !mileage
// option id's has changed
const optionIdsChanged = !!optionIds.filter(a => !prevProps.optionIds.find(b => a === b)).length
// re-calculate on update
if (
!dataIsMissing &&
(contractTemplateId !== prevProps.contractTemplateId ||
duration !== prevProps.duration ||
mileage !== prevProps.mileage ||
optionIdsChanged)
) {
const { contractTemplateId, vehicle, duration, mileage, optionIds, providerPayments, providerShare } = this.props
// [:any] because we're missing the type in SAM-types
let contractType: any
if (!freeContract) {
contractType = {
type: 'STANDARD',
brandId: vehicle!.brand.id,
vehicleModelId: vehicle!.model.id,
fuelTypeId: vehicle!.fuelType.id,
}
} else {
contractType = {
type: 'CUSTOM',
brandName: vehicle!.brand.name,
vehicleModelName: vehicle!.model.name,
fuelTypeName: vehicle!.fuelType.name,
amountPerPayment: 0, // todo: we need this from an input
}
}
const offerData: any = {
...contractType,
contractTemplateId,
duration,
mileage,
registrationDate: vehicle ? formatDate(vehicle.regDate!, { rawFormat: 'YYYY-MM-DD' }) : '',
optionIds,
providerPayments,
providerShare,
}
const response = await this.calculateContractOfferDebounced(offerData)
if (response && response.data) {
this.setState({ offerPrice: response.data, error: '' }, () => {
onCalculationChange(response.data)
})
} else {
const { errorData: error } = response
console.error(error)
this.setState({ error: error!.message })
}
console.log('response', response)
}
}
public render() {
const { classes, visible } = this.props
const { offerPrice } = this.state
return (
<section className={`ContractOfferBreakdown ${visible && 'visible'} `}>
{offerPrice && (
<>
<div className={`ContractOfferBreakdown__price ${classes.price}`}>
<div className="ContractOfferBreakdown__line">
<span className="ContractOfferBreakdown__line-label">{t('Price pr month')}</span>
<span className="ContractOfferBreakdown__line-text">{`${formatCurrency(
offerPrice.amountPrPayment.priceInclVat,
)} ${t('DKK')}`}</span>
</div>
<div className={`ContractOfferBreakdown__price-sub ${classes.priceExVat}`}>{`${formatCurrency(
offerPrice.amountPrPayment.price,
)} ${t('DKK')} ${t('not including VAT')}`}</div>
</div>
<div className="ContractOfferBreakdown__downpayment">
<div className="ContractOfferBreakdown__line">
<span className="ContractOfferBreakdown__line-label">{t('Downpayment total')}</span>
<span className="ContractOfferBreakdown__line-text">{`${formatCurrency(
offerPrice.downpayment.priceInclVat,
)} ${t('DKK')}`}</span>
</div>
</div>
<div className="ContractOfferBreakdown__first-date">
<div className="ContractOfferBreakdown__line">
<span className="ContractOfferBreakdown__line-label">{t('First recurring payment')}</span>
<span className="ContractOfferBreakdown__line-text">{`${formatDate(
offerPrice.firstPaymentDate,
)}`}</span>
</div>
</div>
<div className="ContractOfferBreakdown__first-amount">
<div className="ContractOfferBreakdown__line">
<span className="ContractOfferBreakdown__line-label">{t('Amount')}</span>
<span className="ContractOfferBreakdown__line-text">{`${formatCurrency(
offerPrice.amountPrPayment.priceInclVat,
)} ${t('DKK')}`}</span>
</div>
</div>
<LabelIncludingVat />
</>
)}
</section>
)
}
private calculateContractOfferDebounced = (offerData: any) => api.calculateContractOffer(offerData)
}
export default ContractOfferBreakdown