preface

This article is used to document the collection of pits when using ANDT-related components, and to explore the capabilities of components

Form Usage Guide

1. Bidirectionally bind data to this.props. Form

Keyword: getFieldDecorator
Fields wrapped by getFieldDecorator are automatically bidirectionally bound to the form, and only fields wrapped by getFieldDecorator are
getFieldsValue
getFieldValue
setFieldsValue
demo:
<FormItem {... formItemLayout} label='name'}
>
  {getFieldDecorator('name', {}) (<Input />
  )}
</FormItem>
Copy the code
The name field will be bidirectionally bound to this.props. Form. name, without the need for onChange to explicitly control the value. In submit, all wrapped fields will appear in the form properties.
But if you want to achieve the linkage of the form, can only through the onChange and enclosing props. Form. SetFieldsValue to control other fields, personal feel elegant enough, if we can directly binding to the state relaxed many.


Note:
  1. A form. Item suggests placing only one child decorated by getFieldDecorator. When there are multiple decorated Children,
    help
    required
    validateStatusCannot be generated automatically.
  2. 2.2.0Previously, this was generated automatically only if the Form field was a child of form. Item
    help
    required
    validateStatus, the nesting situation needs to be set by yourself.
  3. You can no longer use value/defaultValue to set the value of the form, only initialValue.
  4. You can’t decorate a pure component, it’s a decorator…


2. Interact form data with upper-layer components

Keywords: mapPropsToFields, onFieldsChange, onValuesChange
MapPropsToFields: Map parent component attributes to form items (can be used to read values from Redux Store)
OnFieldsChange: when
Form.ItemTriggered when the value of the child node changes and the corresponding value can be forwarded to the Redux store
demo:
@Form.create({
  mapPropsToFields(props) {
    // Use the value of the upper component's scope as the data for the form
    const { scope } = props;

    return {
      nickname: Form.createFormField({
        value: scope.nickname,
      }),
      phone: Form.createFormField({
        value: scope.phone,
      }),
      address: Form.createFormField({
        value: scope.address,
      }),
      agreement: Form.createFormField({
        value: scope.agreement,
      }),
    };
  },
  onValuesChange(props, values) {
    // Backfill the change value of the form into the scope of the upper componentprops.onFormChange(values); }})Copy the code
This provides the ability to interact with upper-level components, which is useful for scenarios where the data is in the Store.


3. Rich verification

Key words: rules

demo:

<FormItem {... formItemLayout} label='text'
  extra={this.confirmNode()}
>
  {getFieldDecorator('subscribe', {
    initialValue: subscribe,
    rules: [{
      required: true.message: "message",,})} (<Receiver subscribe={subscribe} onReceiverChange={this.onReceiverChange} />
  )}
</FormItem>
Copy the code
Rules for fields can be easily set using existing validation rules


4. Customize the verification

Keywords: validateFields/validateFieldsAndScroll
demo:
checkFormConfig = (rule, value, cb) = > {
    if (value.length === 0) {
      cb('Rule cannot be empty'); }}... <FormItem > {getFieldDecorator('config', {
    initialValue: 'init value'.rules: [{
      validator: this.checkFormConfig,
    }],
  })(
    <RuleConfig
      alarmConfig={alarmConfig}
    />
  )}
</FormItem>
Copy the code
Value is the value of the field, cb is the verification information displayed when an error occurs, but CB must be called


5. Customize components

Key words: controlled mode
  • Provide controlled properties
    valueOr with other
    valuePropNameProperty with the same name as the value of.
  • provide
    onChangeEvent or
    triggerValue of the event with the same name.
  • Cannot be a functional component.
Using custom components in forms follows the convention above.
Note: When using validations for custom components, you need to be aware of the scope, because ANTD applies validator-error styles to all input fields of faulty components, even to drop-down boxes


6. Form field nesting for custom components

If you have only one form element in your component, you can use it directly as described above, but if the component contains child components, you can’t use FormItem on the outer layer.
. <FormItem></FormItem>
<Parent /> // Instead of wrapping Item around Parent, use it where input is actually used

class Parent extend PureComponent {
    render () {
        return <Child />
    }
}

class Child extend PureComponent {
    render () {
        <FormItem>// Add Item {getFieldDecorator(' Continuous ', {initialValue: Continuous, rules: [{required: true, message: 'Please input u continuous',}, {validator: this.validate,},],}<Select
                  onChange={this.onChangeContinuous}
                >
                  {continuousList}
                </Select>)}</FormItem>}}Copy the code
If it is a child of the map, you need to make sure that the ID of getFieldDecorator is different, or all ids are bound to one value


7. Obtain the value of the user-defined component

For example, the custom component is this
class MyComponent extends PureComponent {
    constructor() {super(a);this.state = {
            receiverList: [].// Controlled properties
        }
    }

    onReceiverChange = receiverList= > {
        this.setState({ receiverList });

        // For the Form callback
        if (this.props.onChange) {
          this.props.onChange(receiverList);
        }
    }

    render() {
        const receiverList = this.state.receiverList;

        return (
            <Input onChange={this.onReceiverChange} value={receiverList}/>); }}Copy the code
In the Form it would look like this:
. <Form><Form.Item>
    {getFieldDecorator('receiverList', {
        rules: [{
            required: true,
            message: 'Please input name!',
        }],
    })(
          <Receiver receiverList />
    )}
    </Form.Item>
</Form>
Copy the code
ReceiverList is a controlled attribute in a custom component. ReceiverList is now a form rule that controls the controlled properties in the custom component


God pit: If it’s a form element, such as SELECT, never write a div outside
<Form>
    <Form.Item>
    {getFieldDecorator('receiverList', {
        rules: [{
            required: true,
            message: 'Please input name!',
        }],
    })(
      //<div>Never add div here, otherwise you can't get the select change event !!!!!<Select>
        <Option value=1>1</Option>
      </Select>
    )}
    </Form.Item>
</Form>Copy the code


ToolTip Usage Guide

1. Click the event bubble

The demo:
<Popconfirm
  title={intl.find('alarm.attributes.sureToDelete')}
  trigger="click"
  onClick={this.onDelete(alarmId)}
>
  <Icon
    type="delete"
  />
</Popconfirm>
Copy the code
A very common example is clicking icon to pop PopConfirm.
However, when you click on the diagram, popConfirm flashes by, executing the onDelete event directly. Why?

Because PopConfirm provides onConfirm, the click event bubbles up when the icon is clicked, so popConfirm triggers onClick.

<Popconfirm
  title={intl.find('alarm.attributes.sureToDelete')}
  trigger="click"
  onConfirm={this.onDelete(alarmId)} // Use the correct listener function
>
  <Icon
    type="delete" onClick={evt= >{ evt.stopPropagation(); // Prevent events from bubbling}} /></Popconfirm>
Copy the code


2. Popconfirm nesting problem

If your design is popConfirm nested, it will look something like this:
<Popover
  placement="bottomRight"
  content={content}
  trigger="click"
  visible={this.state.visible}
  onVisibleChange={this.handVisibleChange}
  overlayClassName="xri-alarm-config-popover"
>
  <i
    className={` ${alarms.length > 0 && alarms[0].alarmObject.status === 'NORMAL' ?
      'alarm-config-icon_useable' : 'alarm-config-icon'} feature-icon`}
    title={intl.find('charts.wrapHead.alarmConfig.title')}
    onClick={this.changVisible}
  />
</Popover>

content:
<Popconfirm
  title='title'
  onConfirm={this.onConfirm}
  onCancel={this.onCancel}
  okText='ok'
  cancelText='cancel'
  visible={this.isShowPopConfirm(index)}
  getPopupContainer={this.getPopupContainer}
  onVisibleChange={this.handVisibleChange}
  overlayClassName="xri-alarm-popconfirm-sync"
>
  <Tooltip title={intl.find('alarm.edit')} trigger="hover">
    <span className="icon" onClick={this.checkSync(item, index)} >
      <Icon type="edit"/>
    </span>
  </Tooltip>
</Popconfirm>
Copy the code
You’ll notice that when you click on the inner PopConfirm layer, you turn off all the outer tooltip controls, which is amazing…
Try to prevent the inner event from bubbling, so that the upper component can’t respond to the event, perfect idea, reasonable, nice
But, incredibly useless, well, on the big move, source code to solve everything.
There is a very important description in the documentation for RC-Tooltip
Function returning html node which will act as tooltip container. By default the tooltip attaches to the body. If you want to change the container, simply return a new element.
The underlying re-trigger reads:
getContainer = () => {
const { props } = this;
const popupContainer = document.createElement(‘div’);
// Make sure default popup container will never cause scrollbar appearing
// https://github.com/react-component/trigger/issues/41
popupContainer.style.position = ‘absolute’;
popupContainer.style.top = ‘0’;
popupContainer.style.left = ‘0’;
popupContainer.style.width = ‘100%’;
const mountNode = props.getPopupContainer ?
props.getPopupContainer(findDOMNode(this)) : props.getDocument().body;
mountNode.appendChild(popupContainer);
return popupContainer;
}
That is, all tooltip-based components have the body as the parent by default, which is why the outer components that prevent bubbling are still affected. When visibleChange is triggered, all components respond, so the solution is: To change the parent relationship, re-Tigger provides an API for changing the default parent. Antd also provides this API for development
getPopupContainerIf you change the parent relationship, your visible events will not affect other components, but pay attention to class, because the hierarchy changes, so the CSS structure needs to be adjusted accordingly.


Upload User Guide

1. Check files

Provides the opportunity to test before upload, you can do the number of files, file type, file size and so on.
function beforeUpload(file, fileList) { const isJPG = file.type === 'image/jpeg'; const isLt2M = file.size / 1024 / 1024 < 2; const isGteMax = fileList.length > 3; if (! isJPG) { message.error('You can only upload JPG file! '); } if (! isLt2M) { message.error('Image must smaller than 2MB! '); } if(isGteMax) { message.error('Image count smaller than 4'); } return isJPG && isLt2M && isGteMax; }... <Upload className="avatar-uploader" name="avatar" showUploadList={false} action="//jsonplaceholder.typicode.com/posts/" beforeUpload={beforeUpload} multiple=true onChange={this.handleChange} > <Button>upload</Button> </Upload>Copy the code


2. Manually upload the file

Upload by default is selected to upload files directly. If you want to manually upload files after completing some business logic, you can use beforeUpload to manually upload files using new FormData() after returning false.
ps:
FormData
import { Upload, Button, Icon, message } from 'antd';
import reqwest from 'reqwest';

class Demo extends React.Component {
  state = {
    fileList: [].uploading: false,
  }

  handleUpload = (a)= > {
    const { fileList } = this.state;
    const formData = new FormData();
    fileList.forEach((file) = > {
      formData.append('files[]', file);
    });

    this.setState({
      uploading: true});// You can use any AJAX library you like
    reqwest({
      url: '//jsonplaceholder.typicode.com/posts/'.method: 'post'.processData: false.data: formData,
      success: (a)= > {
        this.setState({
          fileList: [].uploading: false}); message.success('upload successfully.');
      },
      error: (a)= > {
        this.setState({
          uploading: false}); message.error('upload failed.'); }}); } render() {const { uploading } = this.state;
    const props = {
      action: '//jsonplaceholder.typicode.com/posts/'.onRemove: (file) = > {
        this.setState(({ fileList }) = > {
          const index = fileList.indexOf(file);
          const newFileList = fileList.slice();
          newFileList.splice(index, 1);
          return {
            fileList: newFileList,
          };
        });
      },
      beforeUpload: (file) = > {
        this.setState(({ fileList }) = > ({
          fileList: [...fileList, file],
        }));
        return false;
      },
      fileList: this.state.fileList,
    };

    return (
      <div>
        <Upload {. props} >
          <Button>
            <Icon type="upload" /> Select File
          </Button>
        </Upload>
        <Button
          className="upload-demo-start"
          type="primary"
          onClick={this.handleUpload}
          disabled={this.state.fileList.length= = =0}
          loading={uploading}
        >
          {uploading ? 'Uploading' : 'Start Upload' }
        </Button>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, mountNode);
Copy the code


3. Customize the upload behavior

To customize the long transmission behavior, customRequest is used to customize the default upload action
/* eslint no-console:0 */
import React from 'react';
import ReactDOM from 'react-dom';
import Upload from 'rc-upload';
import axios from 'axios';

const uploadProps = {
  action: '/upload.do'.multiple: false.data: { a: 1.b: 2 },
  headers: {
    Authorization: '$prefix $token',
  },
  onStart(file) {
    console.log('onStart', file, file.name);
  },
  onSuccess(ret, file) {
    console.log('onSuccess', ret, file.name);
  },
  onError(err) {
    console.log('onError', err);
  },
  onProgress({ percent }, file) {
    console.log('onProgress'.`${percent}% `, file.name);
  },
  customRequest({
    action,
    data,
    file,
    filename,
    headers,
    onError,
    onProgress,
    onSuccess,
    withCredentials,
  }) {
    // EXAMPLE: post form-data with 'axios'
    const formData = new FormData();
    if (data) {
      Object.keys(data).map(key= > {
        formData.append(key, data[key]);
      });
    }
    formData.append(filename, file);

    axios
      .post(action, formData, {
        withCredentials,
        headers,
        onUploadProgress: ({ total, loaded }) = > {
          onProgress({ percent: Math.round(loaded / total * 100).toFixed(2) }, file);
        },
      })
      .then(({ data: response }) = > {
        onSuccess(response, file);
      })
      .catch(onError);

    return {
      abort() {
        console.log('upload progress is aborted.'); }}; }};const Test = (a)= > {
  return (
    <div
      style={{
        margin: 100,}} >
      <div>
        <Upload {. uploadProps} >
          <button>To upload</button>
        </Upload>
      </div>
    </div>
  );
};

ReactDOM.render(<Test />, document.getElementById('__react-content'));Copy the code


At the end

Form, ToolTip, and Upload capabilities