РЕАКТ: Должны ли HTML-формы быть контролируемыми или неконтролируемыми компонентами? - PullRequest
0 голосов
/ 20 февраля 2019

У меня возникла дилемма относительно того, где должно жить состояние моей формы.

Реагирует упоминание документов:

Идентифицируйте каждый компонент, который делает что-то на основев этом состоянии.Найдите общий компонент-владелец (один компонент над всеми компонентами, которым требуется состояние в иерархии).Государство должно принадлежать либо общему владельцу, либо другому компоненту, находящемуся выше в иерархии.Если вы не можете найти компонент, в котором есть смысл владеть состоянием, создайте новый компонент просто для хранения состояния и добавьте его где-нибудь в иерархии над компонентом общего владельца.

У меня естьпростой пользовательский интерфейс комментариев.

  1. Пользователь вводит комментарий в текстовую область.
  2. Пользователь вводит имя в поле ввода.
  3. Пользователь нажимает на сообщение, и комментарий отображается ниже.

Компоненты Highrarchy выглядит следующим образом:

 - CommentSection.jsx ---> main (should "Post" state (?))
   -comment-post.jsx  ---> User Comment Form
   - comment-table.jsx
     - comment.jsx 
       - comment-avatar.jsx 
       - comment-body.jsx 

Проблема: В comment-post.jsx поля ввода и textarea имеют обработчик события onChange (), и тамтакже является дескриптором события click на кнопке отправки сообщения.Я могу выбрать одно из двух:

  1. В comment-post.jsx, когда onChange - триггер, отправьте комментарий телу на CommentSection.jsx Проблема здесь будет в том, что я отправлю комментарий телу, как толькопользователь вводит имя, затем отправляет имя позже, когда оно срабатывает, и т. д.
  2. В comment-post.jsx, когда включен триггер onChange. СОХРАНИТЕ значение в состоянии, затем введите имя поля в состоянии, когда пользователь нажимает кнопку отправки.отправить на CommentSection.jsx Преимущество: поля устанавливаются первыми в состоянии, и когда пользователь нажимает на сообщение, они отправляются родителю (как должно быть правильно?)

Должны ли поля ввода комментария, например, commentBody и Name, быть сохранены в comment-post.jsx (компонент формы) или в родительском файле?

Прямо сейчас состояниеЯ делаю 2. Сохранение полей формы в состоянии и затем отправка значений состояния при отправке.

Я думаю, что проблема в onChange и onClick - это два разных обработчика, вопрос в том, какойдолжен передать значения родителюкомпонент в идеале?

    class CommentSection extends Component {
      state = {
        posts: []
      };

      handleCommentPost = post => {
        const posts = this.state.posts;

        posts.push(post);
        this.setState({ posts });
      };

      render() {
        console.log("comment:", this.state.posts);

        return (
          <React.Fragment>
            <h1>comments</h1>
            <div className="row bootstrap snippets">
              <div className="col-md-6 col-md-offset-2 col-sm-12">
                <div className="comment-wrapper">
                  <Comment_Post onClick={this.handleCommentPost} />
                  <Comment_Table comments={this.state.posts} />
                </div>
              </div>
            </div>
          </React.Fragment>
        );
      }
    }

    class Comment_Table extends Component {
       render() {
        const posts = this.props.comments;
        let count = 0;

        return (
          <div className="panel panel-info">
            <hr />
            <ul className="media-list">
              {posts.map(post => (
                <Comment key={count++} comment={post.commentBody} />
              ))}
            </ul>
          </div>
        );
      }
    }



class Comment extends Component {
  render() {
    return (
      <li className="media">
        <Comment_Avatar userAvatar={this.props.commentAvatar} />
        <Comment_Body userComment={this.props.comment} />
      </li>
    );
  }
}

class Comment_Body extends Component {
  render() {
    const { userComment } = this.props;
    return (
      <div className="media-body">
        <span className="text-muted pull-right">
          <small className="text-muted">30 min ago</small>
        </span>
        <strong className="text-success">@MartinoMont</strong>
        <p>
          {userComment}
          {}
        </p>
      </div>
    );
  }
}
class Comment_Post extends Component {
  state = {
    commentBody: null
  };
  onCommentChange = e => {
    this.setState({ commentBody: e.target.value });
  };

  onCommentPost = e => {
    const commentBody = this.state.commentBody;
    if (commentBody !== null) {
      this.props.onClick({ commentBody });
    }
  };
  onNameInput = e => {};

  onCommentPostError() {}

  render() {
    return (
      <React.Fragment>
        <div className="panel-heading p-heading">Comment panel</div>
        <div className="panel-body">
          <textarea
            onChange={this.onCommentChange}
            className="form-control"
            placeholder="write a comment..."
            rows="3"
          />
          <label htmlFor="fname" onChange={this.onNameInput}>
            Name:{" "}
          </label>
          <input id="fname" placeholder="John" />
          <br />
          <button
            type="button"
            className="btn btn-info pull-right"
            onClick={this.onCommentPost}
          >
            Post
          </button>
          <div className="clearfix" />
        </div>
      </React.Fragment>
    );
  }
}

class Comment_Avatar extends Component {
  render() {
    return (
      <a href="#" className="pull-left">
        <img
          src="https://bootdey.com/img/Content/user_1.jpg"
          alt=""
          className="img-circle"
        />
      </a>
    );
  }
}

1 Ответ

0 голосов
/ 20 февраля 2019

Я думаю, что проблема в onChange и onClick - это два разных обработчика, вопрос в том, какой из них в идеале должен передавать значения в родительский компонент?

Существует два типа форм, которые мыиспользовать в стандартном дизайне пользовательского интерфейса.Первый - это когда любое изменение на входе сохраняет изменения.Во-вторых, когда после внесения изменений в элементы формы вы нажимаете кнопку отправки, а затем изменения сохраняются.

Поскольку вы внедрили второй тип, ваш onChange должен иметь дело с управлением состоянием TextArea и вашимonClick должен иметь дело с отправкой.Так что ваш код в порядке.

У меня возникла дилемма относительно того, где должно жить состояние моей формы.

Это зависит ... В вашем случае вы толькоимеют одну форму и два элемента формы, ни один из которых не может использоваться повторно.Таким образом, эти неконтролируемые формы хорошо работают для вас.Однако, если вы хотите иметь компонент Form, который можно использовать повторно, или если вы хотите иметь форму с 15 полями в ней, вы не захотите писать отдельный обработчик onChange для каждого из них.Для этого вы хотели бы создать контролируемый компонент формы, который может обрабатывать все эти вещи для вас.Вот пример того, как это будет выглядеть.

export default class Form extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      values: {}
    };
  }

  @boundMethod
  handleSubmit(event) {
    event.preventDefault();

    this.props.submit(this.state.values);
  }

  @boundMethod
  handleChange(event) {
    const { name, value } = event.target;
    const newValues = Object.assign(
      { ...this.state.values },
      { [name]: value }
    );
    this.setState({
      values: newValues
    });
  }

  public render() {
    const { values } = this.state;
    return (
      <form onSubmit={this.handleSubmit} noValidate={true}>
        <div>
          {React.Children.map(
            this.props.children,
            child => (
                {React.cloneElement(child, {
                  value: values[child.props.name],
                  onChange: this.handleChange
                })}
            )
          )}
          <div>
            <button type="submit">
              Submit
            </button>
          </div>
        </div>
      </form>
    );
  }
}

Тогда вы сможете использовать этот класс Form следующим образом:

<Form
  submit={values => {
    /* work with values */
  }}
>
  <input type="hidden" name="name" />
  <input type="hidden" name="rating" />
</Form>;
...