Ссылка на значение поля в FieldArray для изменения className - PullRequest
0 голосов
/ 02 апреля 2020

У меня есть React Component (изначально написанный кем-то другим), который отображает форму для человека (redux-form) Недавно я изменил компонент на FieldArray (компонент redux-form).

У меня есть проверка для поля электронной почты, которое влияет на className для поля электронной почты (красного цвета, если электронная почта неправильно отформатирована, черный цвет в противном случае). Он работал хорошо, когда он не был FieldArray, но теперь он проверяет все поля электронной почты одновременно из-за

// (in constructor)
this.email = React.createRef();  
// (in Field)  
ref={props.parent.email}  

, т.е. props.parent.email является глобальным / stati c ref.
Пример: есть два человека. Один из них имеет неправильно отформатированный адрес электронной почты, но оба сообщения отображаются красным цветом.

Насколько я понимаю, мне нужен динамический c ref, но это не сработало так, как я пытался ,

ref={`${person}.email.ref`}

Ошибка

"Компоненты функций не могут иметь ссылок. Вы имели в виду использовать React.forwardRef ()?"

Я не сделал Не могу найти ничего полезного в forwardRef в отношении FieldArray, кроме того факта, что он является действительным реквизитом.

Моя цель: когда несколько человек созданы пользователем и / или загружены из магазина Redux, смогут показать каждое правильно отформатированное письмо в черном цвете, и каждое неправильно отформатированное письмо в красном.

Любая помощь очень ценится!

import React from "react";
import { Field, reduxForm, FieldArray } from "redux-form";
import { connect } from "react-redux";
import classNames from 'classnames'
import { MAIL_PATTERN } from "../some_file";
import MoreGeneralComponent from "../some_other_file";

const renderField = ({ input, label, type, options, className, meta: { touched, error }, style, disabled, hidden }) => (
  <div style={style}>
    <label>{label}</label>
    <div>
      <input {...input} disabled={disabled ? true : false} hidden={hidden ? true : false} type={type} placeholder={label} className={className} />
    </div>
  </div>
);

const renderPerson = ({ fields, meta: { error, submitFailed }, ...props }) => {
  setTimeout(props.validate(fields));
  return (
    <ul className="personlist">
      {fields.map((person, index) => (
        <li key={index} className="person">
          <h4>Person #{index + 1}</h4>
          <Field
            component={renderField}
            type="text"
            ref={props.parent.email}
            className={classNames({invalid: !props.parent.state.validEmail})}
            validate={props.parent.validateEmail}
            name={`${person}.email`}
            label="Email"
            key={`${person}.email`}
          ></Field>
          <button type="button" onClick={() => fields.remove(index)}>
            Remove
          </button>
        </li>
      ))}
      {(!(fields.length >= props.max)) && (
        <li className="addperson">
          <button
            type="button"
            onClick={() => fields.push({})}
            disabled={fields.length >= props.max}
          >
            Add Person
        </button>
          {submitFailed && error && <span>{error}</span>}
        </li>)}
    </ul>
  );
};

class Person extends MoreGeneralComponent {
  constructor(props) {
    super(props);
    if (this.state.ready) {
      this.max = 4;
      this.data = ["email"];
      this.email = React.createRef();
    }
  }

  validate = fields => {
    if (!fields || !fields.getAll()) {
      return;
    }
    let valid = true;
    fields.getAll().forEach(field => {
      for (let d of this.data) {
        if (!field[d] || field[d].length < 2) {
          valid = false;
          return;
        } else if (d === "email") {
          valid = field[d] && MAIL_PATTERN.test(field[d]) ? valid : undefined;
        }
      }
    });
    if (valid !== this.state.valid) {
      this.setState({
        valid: valid
      });
    }
  };

  validateEmail = (value) => {
    const valid = value && MAIL_PATTERN.test(value) ? value : undefined;
    this.setState({validEmail: !!valid});
    return valid
  }

  renderQuestion() {
    return (
      <div className={style.question}>
        <fieldset>
          <FieldArray
            name="persons"
            component={renderPerson}
            props={{ max: this.max, validate: this.validate, parent: this }}
            rerenderOnEveryChange={true}
          />
        </fieldset>
      </div>
    );
  }
}

const mapStateToProps = s => {
  const persons = s.persons
  var initialValuesPersons = []
  persons.map(item => initialValuesPersons.push({
    "email": item.email || ""
  }))
  var initialValues = { "persons": initialValuesPersons}
  return {
    initialValues,
    formdata: s.form
  }
}

export default connect(mapStateToProps, null)(reduxForm(
{
  form: 'person',
  destroyOnUnmount: false,
  enableReinitialize: true,
  keepDirtyOnReinitialize: true
})(Person))

1 Ответ

0 голосов
/ 07 апреля 2020

С помощью коллеги я нашел обходной путь, который просто использует JS (обход вокруг с помощью setState).

    toggleClass = (elementName, addInvalid) => {
        const element = document.getElementsByName(elementName);
        element.forEach(item => {
            // remove class and add later, if necessary
            item.classList.remove("invalid");
            if (addInvalid) {
                item.classList.add("invalid");
            }
        })
    }

    validateEmail = (value, person, form, nameField) => {
        const valid = value && MAIL_PATTERN.test(value) ? value : undefined;
        this.toggleClass(nameField, !valid);
        return valid;
    }
...