为什么getInitialState()上不可用的组件props反应Es5?

时间:2018-09-20 10:39:23

标签: reactjs ecmascript-5

我的React组件文件为:-

    var React = require('react');
    var createReactClass = require('create-react-class');
    var ReactScriptLoaderMixin = require('react-script-loader').ReactScriptLoaderMixin;
    var zenApi = require('../../generics/zenApi');
    var swal = require('sweetalert');

    import CreditCardInput from 'react-credit-card-input';

    var PaymentForm = createReactClass({
      billing_post_url  : process.env.API_URL + '/billings/',
      plan_obj_fetch_url: process.env.API_URL + '/billings/getPlan/',
      coupon_check_url  : process.env.API_URL + '/billings/checkCoupon/',

      mixins: [ ReactScriptLoaderMixin ],

      getInitialState: function() {
        return {
          stripeLoading     : true,
          stripeLoadingError: false,
          submitDisabled    : false,
          paymentError      : null,
          paymentComplete   : false,
          token             : null,
          planObj           : {},
          planObjFetchError : false,
          redirectTo        : '',
          cardNumber        : '',
          isInvalid         : false,
          isLoading         : false,
          fullName          : '',
          taxYear           : (new Date()).getFullYear(),
          couponCode        : '',
          isCouponValid     : false,
          backupPlanObj     : {},
          subscriptionStatus: {},
          normalUpgrade     : '',  
          planId            : this.props.plan_id       
        };
      },

      getScriptURL: function() {
        return 'https://js.stripe.com/v2/';
      },

      onScriptLoaded: function() {
        zenApi.get(this.plan_obj_fetch_url + this.state.planId + '?tax_year=' + this.props.taxYear)
          .then(response => {
            if(response.status)
              this.setState({
                planObj           : response.data.plan,
                subscriptionStatus: response.data.subscription_status,
                backupPlanObj     : response.data.plan
              });
            else
              this.setState({
                planObjFetchError: response.status,
              });
          });

        if(!PaymentForm.getStripeToken)
          this.setState({ stripeLoading: false, stripeLoadingError: false });
      },

      onScriptError: function() {
        this.setState({ stripeLoading: false, stripeLoadingError: true });
      },

      onNameChange: function(e) {
        this.setState({
          fullName: e.target.value
        });
      },

      onCouponChange: function(e) {
        this.setState({
          couponCode: e.target.value
        });
      },

      onCouponBlur: function(e) {
        if(e.target.value !== '')
          this.checkCouponCode(this);
        else
          this.setState({ paymentError: null, planObj: this.state.backupPlanObj });
      },

      onTaxYearSelect: function(e) {
        this.setState({
          taxYear: e.target.value
        });
      },

      onSubmit: function(event) {
        Stripe.setPublishableKey(this.props.pub_key);
        let plan_id = this.props.plan_id;
        var self = this;
        event.preventDefault();

        this.setState({ submitDisabled: true, paymentError: null, isLoading: true });

        // send form here
        Stripe.createToken(event.target, function(status, response) {
          if(response.error)
            self.setState({ paymentError: response.error.message, submitDisabled: false, isLoading: false });
          else
            self.postBillings(response.id, plan_id, self);
        });
      },

      postBillings: async(token, plan_id, self) => {
        try {
          zenApi.post(self.billing_post_url,
            {
              stripe_token  : token,
              plan_id       : plan_id,
              upgrade       : self.state.subscriptionStatus.upgrade,
              name          : self.state.fullName,
              year          : self.props.taxYear,
              coupon_code   : self.state.couponCode,
              normal_upgrade: self.props.upgradePlan
            })
            .then(response => {
              if(response.status)
                self.setState({ 
                  paymentComplete: response.status,
                  paymentError   : null
                });
              else
                self.setState({
                  paymentError  : response.data,
                  submitDisabled: false,
                  isLoading     : false
                });
            });
        } catch(err) {
          self.setState({
            paymentError  : err.message,
            submitDisabled: false
          });
        }
      },

      checkCouponCode: async self => {
        try {
          zenApi.post(self.coupon_check_url,
            {
              coupon_code: self.state.couponCode,
              plan_id    : self.props.plan_id
            })
            .then(response => {
              if(response.status)
                self.setState({
                  planObj      : response.data,
                  paymentError : null,
                  isCouponValid: true
                });
              else
                self.setState({
                  planObj      : self.state.backupPlanObj,
                  paymentError : response.message,
                  isCouponValid: false,
                  planObj      : self.state.backupPlanObj
                });
            });
        } catch(err) {
          self.setState({
            paymentError: err.message
          });
        }
      },

      capitalizeFirstLetter(value) {
        var regex = /(\b[a-z](?!\s))/g;
        return value ? value.replace(regex, function(v) {
          return v.toUpperCase();
        }) : '';
      },

      redirectToCallerSource: function() {
        swal({
          title              : 'Payment Successfull!',
          text               : 'Your payment for ' + this.state.planObj.name + ' plan has succeeded.',
          icon               : 'success',
          closeOnEsc         : false,
          closeOnClickOutside: false,
          buttons            : {
            continue: {
              text : 'Continue',
              value: 'continue'
            }
          }
        }).then(btnVal => {
          if(btnVal == 'dashboard') 
            this.setState({ redirectTo: '/dashboard', paymentComplete: false });
          else if(btnVal == 'continue') 
            if(this.props.callUrl == 'wizard') {
              this.setState({ redirectTo: '/paymentSuccessPage?redirectTo=wizard', paymentComplete: false });
            }
            else if(this.props.callUrl == '/') {
              this.setState({ redirectTo: '/paymentSuccessPage?redirectTo=/' });
            }
            else if(this.props.callUrl == 'settings') {
              this.setState({ redirectTo: '/paymentSuccessPage?redirectTo=settings', paymentComplete: false });
            }
            else if(this.props.callUrl == 'marketing') {
              this.setState({ redirectTo: '/paymentSuccessPage?redirectTo=pricing', paymentComplete: false });
            }
            else if(this.props.callUrl == 'dashboard') {
              this.setState({ redirectTo: '/paymentSuccessPage?redirectTo=dashboard', paymentComplete: false });
            }
        });
      },

      handleCardNumberChange: function(e) {
        this.setState({
          cardNumber: e.target.value
        });
      },

      render: function() {
        const cardNumber = this.state.cardNumber;
        const plan_amount = this.state.planObj.amount;
        const subs_status = this.state.subscriptionStatus;

        if(this.state.stripeLoading)
          return <div className="container text-center"><div className="loader" /></div>;
        else if(this.state.stripeLoadingError)
          return <div className="container text-center"><h4>Error</h4></div>;
        else if(this.state.redirectTo != '')
          return <div>{window.location.replace(this.state.redirectTo)}</div>;
        else if(this.state.planObjFetchError)
          return <div className="container text-center"><h4>Error Fetching Plan. Contact Admin.</h4></div>;
        else if(this.state.paymentComplete)
          return <div>{this.redirectToCallerSource()}</div>;
        else if(this.state.planObj.name !== 'Free')
          return (
            <div className="container">
              <div className='row'>
                <div className="col-sm-12 text-center pt-4">
                  <h4>Let's finish powering you up!</h4>
                  <p className="text-grey">You selected {this.state.planObj.name} plan.</p>
                </div>
              </div>

              <div className="row">
                <div className="col-md-12">
                  <div className="payment-form-container pb-5">
                    <div className="flex-dir-row space-between">
                      <h1 className="weight-300">{this.capitalizeFirstLetter(this.state.planObj.name)}</h1>
                      <h1 className="weight-300">{parseFloat(plan_amount - subs_status.discount_amount) ? '$' + parseFloat(plan_amount - subs_status.discount_amount) : '' }</h1>
                    </div>
                    <form onSubmit={this.onSubmit} className="payment-form">
                      {
                        (subs_status.upgrade) ? '' :
                          <div className="row">
                            <div className="col-sm-6 float-left">
                              <div className="form-group">
                                <label htmlFor="coupon_code">Coupon Code</label>
                                <input type='text' className='form-control' name='coupon_code' placeholder='Legit Coupon (If any)' onChange={this.onCouponChange} onBlur={this.onCouponBlur} />
                              </div>
                            </div>
                          </div>
                      }
                      <hr className='decrease-space-between' />
                      <span className="error-text">{ this.state.paymentError }</span><br />
                      <span className="year-text"><i>* You are paying for year <span className='tax-year-text'><b>{ this.state.taxYear }</b></span></i></span>
                      <div className="row">
                        <div className="col-sm-6 float-left">
                          <div className="form-group">
                            <label htmlFor="full_name">Full Name</label>
                            <input type='text' className='form-control' name='full_name' placeholder='Full Name' onChange={this.onNameChange} data-stripe='name' />
                          </div>
                        </div>
                        <div className="col-sm-6 float-right">
                          <div className="form-group">
                            <label htmlFor="formGroupExampleInput">Tax Year</label>
                            <select className="form-control" id="exampleSelect99" onChange={this.onTaxYearSelect}>
                              <option value={this.props.taxYear}>{this.props.taxYear}</option>
                            </select>
                          </div>
                        </div>
                      </div>
                      <div className="row">
                        <div className="col-sm-12">
                          <div className="form-group">
                            <label htmlFor="formGroupExampleInput">Card Number</label>
                            <CreditCardInput
                              data-stripe= 'number'
                              cardNumberInputProps={{ value: cardNumber, onChange: this.handleCardNumberChange, pattern: '(\\d*\\s){3}\\d*', 'data-stripe': 'number' }}
                            />
                          </div>
                        </div>
                      </div>
                      <div className="row">
                        <div className="col-sm-4">
                          <div className="form-group">
                            <label htmlFor="formGroupExampleInput">Expiration month</label>
                            <select className="form-control" id="exampleSelect1" data-stripe='exp-month'>
                              <option>1</option>
                              <option>2</option>
                              <option>3</option>
                              <option>4</option>
                              <option>5</option>
                              <option>6</option>
                              <option>7</option>
                              <option>8</option>
                              <option>9</option>
                              <option>10</option>
                              <option>11</option>
                              <option>12</option>
                            </select>
                          </div>
                        </div>
                        <div className="col-sm-4">
                          <div className="form-group">
                            <label htmlFor="formGroupExampleInput">Expiration year</label>
                            <select className="form-control" id="exampleSelect2" data-stripe='exp-year'>
                              <option>2018</option>
                              <option>2019</option>
                              <option>2020</option>
                              <option>2021</option>
                              <option>2022</option>
                              <option>2023</option>
                              <option>2024</option>
                            </select>
                          </div>
                        </div>
                        <div className="col-sm-4">
                          <div className="form-group">
                            <label htmlFor="formGroupExampleInput" >CVC / CVV</label>
                            <input type='text' data-stripe='cvc' placeholder='cvc' className="form-control form-type-text" maxLength="4" />
                          </div>
                        </div>
                      </div>
                      <hr />
                      <div className="row">
                        {
                          subs_status.upgrade ?
                            <div className="col-sm-12 flex-dir-row space-between mt-3">
                              <p><b>New Plan minus Previous Plan</b></p><h4 className="weight-300">{parseFloat(plan_amount)} - {parseFloat(subs_status.discount_amount)}</h4>
                            </div> : ''
                        }
                        <div className="col-sm-12 flex-dir-row space-between mt-3">
                          <p><b>Total Billed</b></p>
                          {
                            subs_status.upgrade ? <h3 className="weight-300">= {parseFloat(plan_amount - subs_status.discount_amount) ? '$' + parseFloat(plan_amount - subs_status.discount_amount) : ''}</h3> : <h3 className="weight-300">{parseFloat(plan_amount - subs_status.discount_amount) ? '$' + parseFloat(plan_amount - subs_status.discount_amount) : ''}</h3>
                          }
                        </div>
                      </div>
                      <hr />
                      <div className="row">
                        <span className="terms p-left">*All taxes included</span>
                        <div className="col-sm-12 text-center mt-4">
                          <span className="terms">By continuing I agree that I have read and accepted the <a href="#">Term of Use</a> and <a href="#">Privacy Policy</a></span>
                          <button disabled={this.state.submitDisabled} type='submit' value="" className="btn btn-success fullWidth mt-2 flex-dir-row justify-center"><div className={this.state.isLoading ===true ? 'loader-small':''} style={{ margin: '0px' }} />Complete Purchase</button>
                          <img src="/static/images/powered_by_stripe.svg" alt="" className="mt-4" />
                        </div>
                      </div>
                    </form>
                  </div>
                </div>
              </div>
              <style jsx>{`
                .form-type-text{
                  width: 30%;
                  margin-bottom: 5px;
                }
                .p-left{
                  padding-left: 15px;
                }
                .payment-form-container{
                  width: 65%;
                  margin: 0 auto;
                }
                .fullWidth{
                  width: 100%;
                }
                .terms{
                  font-size: 14px;
                }
                .payment-form{
                  margin-top: 10px;
                }
                .error-text{
                  color: #e37775;
                  font-size: 14px;
                }
                .show{
                  display: block;
                }
                .hide{
                  display: none;
                }
                .form-group .form-control{
                  border-radius: 4px;
                  padding: 7px 15px;
                  width: 100%;
                }
                .form-control, select{
                  background: #fff!important;
                  padding: 7px 15px!important;
                }
                .year-text{
                  color: grey;
                  font-size: 0.8em;
                }
                .tax-year-text{
                  color: white;
                  background-color: #00ab84;
                  padding: 0 5px;
                }
                .decrease-space-between{
                  margin-bottom: -20px;
                }
              `}</style>
            </div>
          );
      }
    });

    export default PaymentForm;  

我在'onScriptLoaded'方法中获得未定义的this.state.planId。 getInitialState()方法不是接收道具的最佳位置,如果不是,那么这是通过使用props值进行网络调用的最佳位置。当我通过onScriptLoaded()方法访问道具时,道具有时可用,但有时无法使用,如下面的代码所示:

      onScriptLoaded: function() {
        zenApi.get(this.plan_obj_fetch_url + this.props.plan_id + '?tax_year=' + this.props.taxYear)
          .then(response => {
            if(response.status)
              this.setState({
                planObj           : response.data.plan,
                subscriptionStatus: response.data.subscription_status,
                backupPlanObj     : response.data.plan
              });
            else
              this.setState({
                planObjFetchError: response.status,
              });
          });

        if(!PaymentForm.getStripeToken)
          this.setState({ stripeLoading: false, stripeLoadingError: false });
      },  

getInitialState方法中道具不可用的原因是什么,以及onScriptLoaded方法中道具的随机可用性背后的原因是什么,即有时它可用并且可以工作,但有时它可用并且网络调用将错误提示为404。

我在PaymentFormPage.js文件中具有上述组件调用,如下所示。

    import Layout from '../components/layouts/Main';
    import PaymentForm from '../components/billings/paymentForm';

    class PaymentFormPage extends React.Component {
      constructor() {
        super();
        this.state = {
          plan_id    : '',
          pub_key    : '',
          upgradePlan: '',
          callUrl    : '',
          taxYear    : (new Date()).getFullYear()
        };
      }

      componentDidMount() {
        let request = new URLSearchParams(location.search);

        if(!!request.get('normalUpgrade'))
          this.setState({ upgradePlan: request.get('normalUpgrade') });

        if(!!request.get('callUrl'))
          this.setState({ callUrl: request.get('callUrl') });

        if(!!request.get('taxYear'))
          this.setState({ taxYear: request.get('taxYear') });

        if(!!request.get('plan_id') && !!request.get('key'))
          this.setState({
            plan_id: request.get('plan_id'),
            pub_key: request.get('key')
          });
      }

      render() {
        let plan_id = this.state.plan_id;
        let pub_key = this.state.pub_key;

        return (
          <Layout>
            <div className="container">
              <div className='row'>
                <PaymentForm
                  pub_key={pub_key}
                  plan_id={plan_id}
                  upgradePlan={this.state.upgradePlan}
                  callUrl={this.state.callUrl}
                  taxYear={this.state.taxYear}
                />
              </div>
            </div>
          </Layout>
        );
      }
    }

    export default (PaymentFormPage);  

谢谢大家。

0 个答案:

没有答案