反应:HTML 表单应该是受控组件还是不受控组件?
Posted
技术标签:
【中文标题】反应:HTML 表单应该是受控组件还是不受控组件?【英文标题】:REACT: Should HTML forms be Controlled or Uncontrolled components? 【发布时间】:2019-07-13 17:35:17 【问题描述】:我对我的表单状态应该在哪里生活感到两难。
React 文档提及:
识别基于该状态呈现某些内容的每个组件。 找到一个共同的所有者组件(一个单一的组件高于所有 需要层次结构中状态的组件)。无论是普通 所有者或层次结构中更高的另一个组件应该拥有 状态。如果您找不到一个有意义的组件 state,创建一个简单的新组件来保存状态并添加它 在公共所有者组件上方的层次结构中的某个位置。
我有一个简单的评论 UI。
-
用户在文本区域输入评论。
用户在输入字段中输入姓名。
用户点击post,下方显示评论。
组件等级如下:
- 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
中,输入和文本区域有 onChange() 事件处理程序,并且在提交帖子按钮上还有一个单击事件处理程序。我可以选择以下两种方法之一:
-
在
comment-post.jsx
onChange 被触发时,将commentBody 发送到CommentSection.jsx
这里的问题是我会在用户输入后立即发送commentBody,然后在触发时发送名称等等强>
在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"
className="img-circle"
/>
</a>
);
【问题讨论】:
【参考方案1】:我认为问题在于 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>;
【讨论】:
以上是关于反应:HTML 表单应该是受控组件还是不受控组件?的主要内容,如果未能解决你的问题,请参考以下文章