重复点击按钮

重复点击按钮会导致重复提交表单,从而产生重复的数据。我们可以有多种方式解决,比如在数据库添加唯一索引。在这里,提供另一种方式,在前端控制用户重复点击。

前端用的是react + antd,因此案例代码与react相关,但思想是一致的。

提供公共组件:

定义了state: buttonDisabled, updateButtonDisable function

import React from 'react';
import { Button } from 'antd';
import PropTypes from 'prop-types';


/**
 * This component is created to implement the requirement
 * disabling submit button until the request is completed
 */

class SubmitButton extends React.Component {
  state = {
    buttonDisabled: this.props.initialDisabledStatus,
  };

  componentDidUpdate(prevProps) {
    const { initialDisabledStatus } = this.props;
    if (prevProps.initialDisabledStatus !== initialDisabledStatus) {
      this.updateButtonDisabled(initialDisabledStatus);
    }
  }

  updateButtonDisabled = (disable) => {
    this.setState({ buttonDisabled: disable });
  }

  render() {
    const { buttonDisabled } = this.state;
    const { initialDisabledStatus, ...rest } = this.props;

    return (
      <Button
        {...rest}
        style={buttonDisabled ? { cursor: 'progress' } : null}
        disabled={buttonDisabled}
        onClick={e => this.props.onClick(e)}
      >
        {this.props.children}
      </Button>
    );
  }
}

SubmitButton.defaultProps = {
  initialDisabledStatus: false,
};

SubmitButton.propTypes = {
  onClick: PropTypes.func.isRequired,
  initialDisabledStatus: PropTypes.bool,
};

export default SubmitButton;

 引用SubmitButton组件, 添加ref,这是为了获取SubmitButton组件实例。

在handleSubmit函数中,将this.submitButton.updateButtonDisabled传给sendmail函数

 handleSubmit = () => {
    const { form, actions } = this.props;
    form.validateFields((err, values) => {
      if (!err) {
        actions.sendEmail(values.email, this.submitButton.updateButtonDisabled);
      }
    });
  }


<Form>
  <Form.Item>
    <SubmitButton ref={(e) => { this.submitButton = e; }} onClick={this.handleSubmit}>Submit</SubmitButton>
  </Form.Item>
</Form>

在发送请求之前,call setSubmitButtonDisabled(true)去disable button,当请求结束或发生错误时,再 call setSubmitButtonDisabled(false)使按钮又可以点击。

export const sendEmail = (emailAddress, setSubmitButtonDisabled) => ((dispatch) => {
  setSubmitButtonDisabled(true);
  createResource('/api/account/password/reset', { email: emailAddress }).then(() => {
    setSubmitButtonDisabled(false);
    history.push('/account/password/reset/done');
  }).catch((err) => {
    setSubmitButtonDisabled(false);
    dispatch(setErrorMsgAction(true, err.entity.message));
  });
});

以上案例是基本案例。但有时我们会遇到如下情况:

对表单中的某个字段进行后端校验,如果存在就不能创建,反之就可以创建。如果是这种情况,上述案例就不能控制住,因为这时有校验和创建两个异步的action,难以控制,因此,我采用了另一种方式,基于方式一的基础上:

此案例:

实现对assement的复制,名字不能重复。

AppModal和SubmitButton本质是一样的

import React from 'react';
import PropTypes from 'prop-types';
import { Modal } from 'antd';
import classnames from 'classnames';

class AppModal extends React.Component {
  state = {
    buttonDisabled: false,
  };

  updateButtonDisabled = (disable) => {
    this.setState({ buttonDisabled: disable });
  }

  render() {
    const { buttonDisabled } = this.state;
    return (
      <Modal
        {...this.props}
        maskClosable={false}
        wrapClassName={classnames(`common-modal-style ${this.props.wrapClassName}`, { 'submit-disabled': buttonDisabled })}
        maskStyle={{ backgroundColor: 'rgba(240, 242, 245, 0.8)' }}
        okButtonProps={{
          disabled: buttonDisabled ||
              (this.props.okButtonProps && this.props.okButtonProps.disabled),
        }}
        onOk={e => this.props.onOk(e)}
      >
        {this.props.children}
      </Modal>
    );
  }
}

AppModal.defaultProps = {
  title: ' ', // if title is not set, the style will be broken
  destroyOnClose: true,
  visible: false,
};

AppModal.propTypes = {
  title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
  destroyOnClose: PropTypes.bool,
  visible: PropTypes.bool,
  onCancel: PropTypes.func.isRequired,
  children: PropTypes.node.isRequired,
};

export default AppModal;

定义了state: {fieldsHasError: true, assessmentDuplicate: true, assessmentName: ''}, assessmentValidateCallBack是当后端校验有错误时的回调函数,更新state的值。

这里,提交按钮绑定的函数是duplicateAssessment,该函数实际上调用的是actions.checkIfAssessmentAlreadyExists,用来校验name是否重复,真正执行创建功能是再componentDidUpdate周期里,当!fieldsHasError && !assessmentDuplicate 为true时,才真正的复制assessment。

import React from 'react';
import AppModal from 'App/Common/Modal/Components/AppModal';
import { Form, Input } from 'antd';
import PropTypes from 'prop-types';
import { FIELD_MAX_LENGTH } from 'App/Common/Constants';

class DuplicateModal extends React.Component {
  state = {
    fieldsHasError: true,
    assessmentDuplicate: true,
    assessmentName: '',
  };

  componentDidUpdate() {
    const { fieldsHasError, assessmentDuplicate, assessmentName } = this.state;
    const {
      currentSelect, actions, pagination,
    } = this.props;
    const params = {
      id: currentSelect.id,
      name: assessmentName,
    };

    if (!fieldsHasError && !assessmentDuplicate) {
      actions.duplicateAssessment(
        params,
        pagination.current,
        this.appModal.updateButtonDisabled,
      );
      this.setState({
        fieldsHasError: true,
        assessmentDuplicate: true,
        assessmentName: '',
      });
    }
  }

  onCancel= () => {
    this.props.actions.duplicateModalVisibleSwitch(false);
  }

  assessmentValidateCallBack = (message) => {
    const { form } = this.props;
    if (message) {
      form.setFields({
        name:
            {
              value: form.getFieldValue('name'),
              errors: [new Error(message)],
            },
      });
    }
    this.setState({ assessmentDuplicate: message !== undefined && message !== null });
  };

  duplicateAssessment = () => {
    const {
      form, actions,
    } = this.props;
    const fieldName = ['name'];
    form.validateFieldsAndScroll(fieldName, (err, values) => {
      if (!err) {
        actions.checkIfAssessmentAlreadyExists(
          values.name,
          this.assessmentValidateCallBack,
          this.appModal.updateButtonDisabled,
        );
        this.setState({ fieldsHasError: err, assessmentName: values.name });
      }
    });
  };

  render() {
    const { form, visible } = this.props;
    const { getFieldDecorator } = form;

    return (
      <AppModal ref={(e) => { this.appModal = e; }} title="Duplicate Assessment" visible={visible} onOk={this.duplicateAssessment} onCancel={this.onCancel}>
        <Form>
          <Form.Item label="New Assessment name">
            {getFieldDecorator('name', {
                rules: [{
                  required: true, message: 'Please enter a new assessment name', whitespace: true,
                }],
              })(<Input maxLength={FIELD_MAX_LENGTH.NAME} />)}
          </Form.Item>
        </Form>
      </AppModal>
    );
  }
}

DuplicateModal.propTypes = {
  form: PropTypes.shape().isRequired,
  actions: PropTypes.shape().isRequired,
  currentSelect: PropTypes.shape().isRequired,
  pagination: PropTypes.shape().isRequired,
  visible: PropTypes.bool.isRequired,
};

export default Form.create()(DuplicateModal);

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值