Как получить проверку ввода и отключить/включить кнопку отправки в зависимости от состояния формы в ReactJS

Я использую npm Validator и пытаюсь проверить введенную информацию, а затем изменить состояние кнопки отправки адреса в зависимости от проверки. То, как я это настроил сейчас, я могу печатать, и текст отображается в консоли, но не в пользовательском интерфейсе. Я не уверен, что я не так. Кнопка также не изменится на «включено».

Вот компонент:

import React, {Component} from 'react';
import { CardElement, injectStripe } from 'react-stripe-elements';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { clearCart } from '../actions/clearCartAction';
import getTotal from '../helpers/getTotalHelper';
import { Container, Col, Form, FormGroup, Input, Button } from 'reactstrap';
import './StripeCheckoutForm.css';
import validator from 'validator';

const cardElement = {
  base: {
    color: '#32325d',
    width: '50%',
    lineHeight: '30px',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '18px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

const FIREBASE_FUNCTION = 'https://us-central1-velo-velo.cloudfunctions.net/charge/';

// Function used by all three methods to send the charge data to your Firebase function
async function charge(token, amount, currency) {
  const res = await fetch(FIREBASE_FUNCTION, {
      method: 'POST',
      body: JSON.stringify({
          token,
          charge: {
              amount,
              currency,
          },
      }),
  });
  const data = await res.json();
  data.body = JSON.parse(data.body);
  return data;
}

class CheckoutForm extends Component {
  constructor(props) {
    super(props);
    this.submit = this.submit.bind(this);
  }

  state = {
    paymentComplete: false,
    firstName: '',
    lastName: '',
    address: '',
    city: '',
    prefecture: '',
    zipCode: '',
    email: '',
    submitDisabled: true
  }

  inputChangeHandler = (event, name) => {
    console.log('inputChange', name, event.target.value)
    event.preventDefault()
    this.setState({
      name: event.target.value
    }, console.log(this.state.submitDisabled), function(){ this.canSubmit() })
  }

  canSubmit = () => {
    console.log("canSubmit")
    const { firstName, lastName, address, city, prefecture,zipCode, email} = this.state
    if (validator.isAlpha(firstName) 
    && validator.isAlpha(lastName) 
    && address > 0
    && validator.isAlpha(city)
    && validator.isAlpha(prefecture)
    && zipCode > 0
    && validator.isEmail(email)) {
      this.setState({submitDisabled: false})
    } else {
      this.setState({submitDisabled: true})
    }
  }

  clearCartHandler = () => {
    console.log('clearCartHandler');
    this.props.onClearCart()
  }

  // User clicked submit
  async submit(ev) {
    console.log("clicked!")
    const {token} = await this.props.stripe.createToken({name: "Name"});
    const total = getTotal(this.props.cartItems);
    const amount = total; // TODO: replace with form data
    const currency = 'USD';
    const response = await charge(token, amount, currency);

    if (response.statusCode === 200) {
      this.setState({paymentComplete: true});
      console.log('200!!',response);
      this.clearCartHandler();

    } else {
      alert("wrong credit information")
      console.error("error: ", response);
    }
  }

  render() {

    if (this.state.complete) {
      return (
        <div>
          <h1 className="purchase-complete">Purchase Complete</h1>
          <Link to='/'>
            <button>Continue Shopping</button>
          </Link>
        </div>
      );
    }

    return ( 
      <div className="checkout-wrapper"> 
      <Container className="App">
        <h2 className='text-center'>Let's Checkout</h2>
          <Form className="form">
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="first name"
                  name={"first name"}
                  value={this.state.firstName}
                  id="exampleEmail"
                  placeholder="first name"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="last name"
                  name="last name"
                  value={this.state.lastName}
                  id="exampleEmail"
                  placeholder="last name"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="address"
                  name="address"
                  value={this.state.adress}
                  id="exampleEmail"
                  placeholder="address"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="city"
                  name="city"
                  value={this.state.city}
                  id="exampleEmail"
                  placeholder="city"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="prefecture"
                  name="prefecture"
                  value={this.state.prefecture}
                  id="exampleEmail"
                  placeholder="prefecture"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="zipcode"
                  name="zipcode"
                  value={this.state.zipCode}
                  id="exampleEmail"
                  placeholder="zipcode"
                />
              </FormGroup>
            </Col>
            <Col>
              <FormGroup>
                <Input
                  onChange= {this.inputChangeHandler}
                  type="email"
                  name="email"
                  value={this.state.email}
                  id="exampleEmail"
                  placeholder="[email protected]"
                />
              </FormGroup>
            </Col>
            <Button className="save-address-button" disabled={this.state.submitDisabled}>Submit Address</Button>
            <div className="card-element">
            <CardElement style={cardElement}/>
            </div>
          </Form>
        <button className="checkout-button" disabled={false} onClick={this.submit}>Submit</button>
      </Container>
      </div> 
    );
  }
}

const mapStateToProps = state => {
  return {
    cartItems: state.shoppingCart.cartItems
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onClearCart: () => dispatch(clearCart())
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(injectStripe(CheckoutForm));

person frootloops    schedule 01.01.2019    source источник


Ответы (1)


Я думаю, что ошибка связана с неправильным использованием функции обратного вызова this.setState.

В вашем методе inputChangeHandler вы передали три аргумента в this.setState(), однако this.setState() ожидает, что вы передадите не более двух аргументов, причем вторым аргументом будет функция обратного вызова.

Поскольку вы передаете свою функцию обратного вызова в качестве третьего аргумента, this.setState() молча игнорирует ее, поэтому ваш метод проверки не вызывается.

Другая проблема связана с параметром имени для inputChangeHandler, inputChangeHandler будет получать только объект события, а не имя. Чтобы получить доступ к атрибуту имени элемента ввода, вам нужно будет получить к нему доступ через event.target.name

Чтобы решить проблемы, вы можете изменить inputChangeHandler на

inputChangeHandler = event => {
    const { name, value } = event.target;
    console.log('inputChange', event.target.name, event.target.value)

    // notice the square bracket, this means we use the computed variable as 
    // the object key, instead of using the string 'name' as object key
    // also the name attribute in your Input element must match your state's key
    // use name='lastName' instead of name='last name' as your state's key is lastName
    this.setState({
      [name]: value
    }, this.canSubmit) // changing this line
}

Если вы хотите сохранить свой console.log(this.state.submitDisabled), вы можете добавить его в свой метод проверки.

canSubmit = () => {
    console.log("canSubmit", this.state.submitDisabled) // add it here
    const { firstName, lastName, address, city, prefecture, zipCode, email} = this.state
    if (validator.isAlpha(firstName) 
    && validator.isAlpha(lastName) 
    && address > 0
    && validator.isAlpha(city)
    && validator.isAlpha(prefecture)
    && zipCode > 0
    && validator.isEmail(email)) {
      this.setState({submitDisabled: false})
    } else {
      this.setState({submitDisabled: true})
    }
}

В методе рендеринга, я думаю, вы имеете в виду

if (this.state.paymentComplete)

вместо

if (this.state.complete)

Кроме того, кнопка внизу, вы жестко запрограммировали отключенное значение атрибута

<button
    className="checkout-button"
    disabled={false}
    onClick={this.submit}
>
    Submit
</button>

может ты имеешь в виду

<button
    className="checkout-button"
    disabled={this.state.submitDisabled}
    onClick={this.submit}
>
    Submit
</button>

В качестве примечания: атрибут типа для элемента ввода должен быть «текст», «число», «флажок» и т. д. Вместо случайного текста, такого как «фамилия» или «префектура».

person Ray Chan    schedule 01.01.2019
comment
Спасибо за вашу помощь, но я попробовал это, и у меня все еще та же проблема. - person frootloops; 01.01.2019
comment
Вызывается ли метод canSubmit и обновляется ли состояние сейчас? - person Ray Chan; 01.01.2019
comment
@frootloops Я обновил ответ, может быть, вы можете проверить, работает ли он сейчас - person Ray Chan; 01.01.2019
comment
Спасибо, что указали на них. Я все еще не могу заставить тип отображаться во входных данных. Однако я вижу каждое нажатие клавиши и букву в консоли. - person frootloops; 01.01.2019
comment
@frootloops Проблема должна быть связана с inputChangeHandler, я снова обновил ответ, пожалуйста, дайте мне знать, работает ли он сейчас или есть еще какое-то неожиданное поведение. - person Ray Chan; 01.01.2019
comment
Я наконец нашел проблему. Основная проблема заключалась в том, что некоторые имена входных данных не соответствовали элементам в состоянии. и [event.target.name]: event.target.value. Спасибо за вашу помощь @Ray Chan - person frootloops; 01.01.2019