REACT:HTML表单应该是受控制的还是不受控制的组件?

问题描述 投票:2回答:1

关于我的表格状态应该存在的地方我处于两难境地。

React Docs提到:

根据该状态识别呈现某些内容的每个组件。查找公共所有者组件(在层次结构中需要状态的所有组件上方的单个组件)。公共所有者或层次结构中较高层的其他组件应该拥有该状态。如果找不到拥有状态的组件,只需创建一个新组件来保存状态,并将其添加到公共所有者组件上方的层次结构中的某个位置。

我有一个简单的评论UI。

  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中,input和textarea字段具有onChange()事件处理程序,并且在提交帖子按钮上还有一个单击事件句柄。我可以选择做两个中的一个:

  1. comment-post.jsx中,当onChange触发时,将commentBody发送到CommentSection.jsx这里的问题是我将在用户输入后立即发送commentBody,然后在触发时发送名称,依此类推
  2. comment-post.jsx中,当onChange触发SAVE状态值时,然后在状态中命名名称字段,当用户单击提交按钮发送到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>
    );
  }
}
javascript reactjs user-interface components reactive-programming
1个回答
0
投票

我认为问题是onChange和onClick是两个不同的处理程序,问题是哪个应该理想地将值传递给父组件?

我们在标准用户界面设计中使用了两种类型的表单。首先是输入中的任何更改保存更改。其次,在更改表单元素后,按下提交按钮,然后保存更改。

由于你已经实现了第二种类型,你的onChange应该处理控制你的TextArea状态,你的onClick应该处理提交。所以你的代码很好。

关于我的表格状态应该存在的地方我处于两难境地。

这取决于...在您的情况下,您只有一个表单和两个表单元素,其中任何一个都不可重用。所以这些不受控制的形式对你有用。但是,如果您希望有一个可重复使用的表单组件,或者如果您想要一个包含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>;
© www.soinside.com 2019 - 2024. All rights reserved.