Заметки о боевых монстрах AntD

внешний интерфейс Ресурсы изображений React.js Redux
Заметки о боевых монстрах AntD

предисловие

Эта статья используется для записи сбора ям для майнинга при использовании компонентов, связанных с andt, и определения границ возможностей компонентов.

Руководство пользователя формы

1. Двусторонняя привязка данных к this.props.form

Ключевые слова: GetFieldDecorator
Используйте поля, которые получают пакеты FetfieldDecorator автоматически, и форма является бидвардной, и доступна только упаковка getfieldDecorator.getFieldsValue getFieldValue setFieldsValueЖдать
demo:
<FormItem
  {...formItemLayout}
  label='name'}
>
  {getFieldDecorator('name', {})(
    <Input />
  )}
</FormItem>
В это время имя поля будет двунаправленно привязано к this.props.form.name, и нет необходимости, чтобы onChange явно управлял значением.При отправке все обернутые поля появятся в свойствах формы.
Однако, если вы хотите реализовать привязку формы, вы можете управлять другими полями только через onChange и this.props.form.setFieldsValue. Лично я считаю, что это недостаточно элегантно. Если его можно напрямую привязать к состоянию, это будет намного освежать.


Уведомление:
  1. В Form.Item рекомендуется помещать только один дочерний элемент, украшенный с помощью getFieldDecorator, когда есть несколько украшенных дочерних элементов,help required validateStatusНе может быть сгенерирован автоматически.
  2. 2.2.0Ранее он автоматически генерировался только в том случае, если поле формы было дочерним по отношению к Form.Item.help required validateStatus, ситуацию гнездования необходимо установить самостоятельно.
  3. Вы больше не можете использовать value/defaultValue для установки значения формы, вы можете использовать только initialValue для его установки.
  4. Вы не можете украсить чистый компонент, в конце концов, это декоратор. . .


2. Взаимодействие данных формы с компонентами верхнего уровня

Ключевые слова: mapPropsToFields, onFieldsChange, onValuesChange
mapPropsToFields: сопоставление свойств родительского компонента с элементами формы (можно использовать для считывания значений в хранилище Redux)
onfieldschange: когдаForm.ItemЗапускается при изменении значения дочернего узла, вы можете сбросить соответствующее значение в хранилище Redux.
demo:
@Form.create({
  mapPropsToFields(props) {
    // 使用上层组件的scope的值作为表单的数据
    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) {
    // 将表单的变化值回填到上层组件的scope中
    props.onFormChange(values);
  },
})
Это дает возможность взаимодействовать с компонентами верхнего уровня, что полезно для сценариев, в которых данные находятся в хранилище.


3. Богатая проверка

Ключевые слова: правила
demo:
<FormItem
  {...formItemLayout}
  label='text'
  extra={this.confirmNode()}
>
  {getFieldDecorator('subscribe', {
    initialValue: subscribe,
    rules: [{
      required: true,
      message: ‘message’,
    }],
  })(
    <Receiver subscribe={subscribe} onReceiverChange={this.onReceiverChange} />
  )}
</FormItem>
Правила поля можно легко установить с помощью существующих правил проверки.


4. Пользовательская проверка

Ключевые слова: валидатефиелдс/валидатефиелдсандскролл
demo:
checkFormConfig = (rule, value, cb) => {
    if (value.length === 0) {
      cb('规则不能为空');
    }
}
...
<FormItem >
  {getFieldDecorator('config', {
    initialValue: 'init value',
    rules: [{
      validator: this.checkFormConfig,
    }],
  })(
    <RuleConfig
      alarmConfig={alarmConfig}
    />
  )}
</FormItem>
value — значение поля, cb — проверочная информация, отображаемая при возникновении ошибки, но cb необходимо вызывать


5. Пользовательские компоненты

Ключевые слова: управляемый режим
  • Предоставление контролируемых свойствvalueили другойvaluePropNameОдноименное значение.
  • поставкаonChangeсобытие илиtriggerЗначение события с тем же именем.
  • Не может быть функциональным компонентом.
Использование пользовательских компонентов в формах следует приведенным выше соглашениям.
Примечание. При использовании проверки для пользовательских компонентов необходимо обращать внимание на область действия, поскольку antd добавит стиль валидатора-ошибки во все поля ввода компонента ошибки, и даже в раскрывающемся списке будут напоминания.


6. Вложенность полей формы пользовательских компонентов

Если в вашем компоненте есть только один элемент формы, вы можете использовать непосредственное использование вышеперечисленного, но компоненты содержат SUMS, вы не можете использовать FormiteM во внешнем слое.
...
<FormItem>
</FormItem>
<Parent /> //不用在Parent外面包装Item,在实际使用input的地方使用

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

class Child extend PureComponent {
    render () {
        <FormItem> // 在实际的输入上加Item
          {
            getFieldDecorator('continuous', {
              initialValue: continuous,
              rules: [
                {
                  required: true,
                  message: 'please input u continuous',
                },
                {
                  validator: this.validate,
                },
              ],
            })(
                // 绝对不用在这里包裹div,不然就接收不到change
                <Select
                  onChange={this.onChangeContinuous}
                >
                  {continuousList}
                </Select>
            )
          }
        </FormItem>
    }
}
Если это подкомпонент с карты, вам нужно убедиться, что идентификатор getFieldDecorator отличается, иначе все идентификаторы привязаны к значению


7. Получите значение пользовательского компонента

Например, пользовательский компонент — это
class MyComponent extends PureComponent {
    constructor(){
        super();
        this.state = {
            receiverList: [],// 受控属性
        }
    }

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

        // 用于Form的回调
        if (this.props.onChange) {
          this.props.onChange(receiverList);
        }
    }

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

        return (
            <Input onChange={this.onReceiverChange} value={receiverList}/>
        );
    }
}
Просто напишите это в форме:
...
<Form>
    <Form.Item>
    {getFieldDecorator('receiverList', {
        rules: [{
            required: true,
            message: 'Please input name!',
        }],
    })(
          <Receiver receiverList />
    )}
    </Form.Item>
</Form>
ReceiverList — это управляемое свойство в пользовательском компоненте. Теперь контролируемое свойство в пользовательском компоненте назначено на form.receiverList, и им также можно управлять по правилу формы, разве не здорово~

Божья яма: если это элемент формы, такой как select, никогда не пишите div снаружи
<Form>
    <Form.Item>
    {getFieldDecorator('receiverList', {
        rules: [{
            required: true,
            message: 'Please input name!',
        }],
    })(
      //<div> 绝对不要在这里加div,否则不能获取select的change事件!!!!!
      <Select>
        <Option value=1>1</Option>
      </Select>
    )}
    </Form.Item>
</Form>


Руководство пользователя всплывающей подсказки

1. Нажмите всплывающую подсказку

демо:
<Popconfirm
  title={intl.find('alarm.attributes.sureToDelete')}
  trigger="click"
  onClick={this.onDelete(alarmId)}
>
  <Icon
    type="delete"
  />
</Popconfirm>
Очень распространенный пример: при нажатии на значок появляется всплывающее окно подтверждения.
Однако, когда вы щелкнете по диаграмме, вы обнаружите, что popconfirm мелькает и непосредственно выполняет событие onDelete.Почему?
Поскольку popconfirm предоставляет onConfirm, при нажатии на значок всплывает событие click, поэтому onClick запускается в popconfirm. Зная процесс, восстановление также очень просто, а именно:
<Popconfirm
  title={intl.find('alarm.attributes.sureToDelete')}
  trigger="click"
  onConfirm={this.onDelete(alarmId)} // 使用正确的监听函数
>
  <Icon
    type="delete" onClick={evt => {
      evt.stopPropagation(); // 阻止事件冒泡
    }}
  />
</Popconfirm>


2. Проблема попконфирминги в гнездо

Если ваш проект дизайна имеет вложенную связь с popconfirm, это выглядит следующим образом:
<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='取消'
  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>
Вы обнаружите, что когда вы нажимаете внутреннее всплывающее окно, все элементы управления класса всплывающей подсказки во внешнем слое отключаются, что удивительно. . .
Постарайтесь предотвратить всплытие внутреннего события, чтобы верхний компонент не реагировал на событие, идеальная идея, разумная, приятная.
Впрочем, бесполезно, ну большой ход, исходники все решают.
В документации 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.
основаниеre-triggerЭто написано:
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;
}
Другими словами, все компоненты на основе всплывающих подсказок имеют тело в качестве родительского компонента по умолчанию, поэтому внешние компоненты, которые предотвращают всплытие, по-прежнему затрагиваются. на самом деле re-tigger предоставляет API для изменения родителя по умолчанию, а antd также предоставляет этот API для разработки, т.е.getPopupContainer, после изменения родительского отношения ваше видимое событие не повлияет на другие компоненты, но обратите внимание на класс, потому что меняется уровень, поэтому структуру css необходимо соответствующим образом скорректировать.


загрузить руководство по использованию

1. Обнаружение файлов

Предоставляет возможность обнаружения перед загрузкой, а также может определять количество файлов, типы файлов, размеры файлов и т. д.
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>


2. Ручная загрузка

загрузка выбрана по умолчанию для прямой загрузки файлов.Если вы хотите загружать вручную после завершения некоторой бизнес-логики, вы можете использовать beforeUpload для возврата false, загружать файлы вручную и использовать new FormData();
import { Upload, Button, Icon, message } from 'antd';
import reqwest from 'reqwest';

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

  handleUpload = () => {
    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: () => {
        this.setState({
          fileList: [],
          uploading: false,
        });
        message.success('upload successfully.');
      },
      error: () => {
        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);


3. Настройте поведение загрузки

Когда вам нужно настроить поведение длинной загрузки, вы в основном используете customRequest для реализации пользовательского действия загрузки по умолчанию.
/* 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 = () => {
  return (
    <div
      style={{
        margin: 100,
      }}
    >
      <div>
        <Upload {...uploadProps}>
          <button>开始上传</button>
        </Upload>
      </div>
    </div>
  );
};

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


конец

На данный момент границы возможностей Form, ToolTip и Upload обобщены, и я надеюсь, что это будет полезно для всех~