在ReactJs中取消网络请求

时间:2018-08-15 13:29:01

标签: reactjs typescript asynchronous lodash debouncing

我有一个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

0 个答案:

没有答案