为啥创建评论部分时我的状态没有按预期更新?
Posted
技术标签:
【中文标题】为啥创建评论部分时我的状态没有按预期更新?【英文标题】:Why is my state not updating as expected when creating a comment section?为什么创建评论部分时我的状态没有按预期更新? 【发布时间】:2020-01-25 19:14:36 【问题描述】:我正在尝试创建一个评论部分,其中this.state
存储每次提交新评论时通过将旧状态与新状态组合而成的所有 cmets。但是,我在我的应用程序中遇到了非常奇怪的行为,我无法解释。在提交第一个评论时,评论从commentForm
发送到它的父commentSection
,使用调用父函数handleCommentSubmit
的回调,更新我的comments
状态。一切都正确呈现。但是,一旦提交了第二条评论,就会发生相同的过程;状态已更新,但不包含先前的第一条评论。在此状态更新之后,新状态将发送到 commentList
以呈现 cmets。在这种情况下,奇怪的是,传递给它的 prop this.state.comments
现在包含一个数组,其中包含第二条评论的 2(截图见底部)。这导致我的评论部分现在显示用户 2 的评论两次,而不显示用户 1 的评论。有谁知道为什么会这样?
相关代码如下:
家长:
class CommentsSection extends React.Component
constructor(props)
super(props)
this.state=comments:[], loading:false
componentDidMount()
handleCommentSubmit = (newComment) =>
var comments = this.state.comments;
var newComments = comments.concat([newComment]);
this.setState(comments: newComments,console.log('The current state is now',this.state.comments));
//comment is object with author and message. Add new comment to old comments
//this.setState(comments:[...this.state.comments,newComment],console.log(this.state, 'state updated'))
//Comments are create in comment form, passed up then sent down through commentList to individual comment rendering inside comment.js
// comment form oncommentsubmit running everytime it renders, not only on submital
render()
const loadingSpin = this.state.loading ? "App-logo Spin" : "App-logo";
return(
<div>
<span><h4> Comments </h4></span>
<div className="ui grid">
<div className = "right floated eight wide column" >
<CommentList comments=this.state.comments/>
</div>
<div className="left floated eight wide column">
<CommentForm onCommentSubmit=this.handleCommentSubmit/>
</div>
</div>
</div>
)
export default CommentsSection
孩子们:
function CommentList (comments)
//need to map over array of comments to format correctly
console.log('This is what is passed as props to CommentList', comments)
comments = comments.map((comments)=>return <Comment key = comments.message message=comments.message author=comments.author />)
return(<div>comments</div>)
export default CommentList
class CommentForm extends React.Component
constructor(props)
super(props)
this.comment=author:'', message:''
handleSubmit= (e)=>
e.preventDefault()
var authorVal = this.comment.author;
var textVal = this.comment.message;
//this stops any comment submittal if anything missing
if (!textVal || !authorVal)
return;
this.props.onCommentSubmit(this.comment);
//reset form values
e.target[0].value = '';
e.target[1].value = '';
handleFormChange= (e)=>
e.preventDefault()
if(e.target.name==='author')
var author = e.target.value.trim();
this.comment.author = author
else if(e.target.name==='message')
var message = e.target.value.trim();
this.comment.message = message
render()
return (
<form className = "ui form" method="post" onChange=(e)=>this.handleFormChange(e) onSubmit=(e)=>this.handleSubmit(e)>
<div className="form-group">
<input
className="form-control"
placeholder="user..."
name="author"
type="text"
/>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="comment..."
name="message"
/>
</div>
<div className="form-group">
<button disabled=null className="btn btn-primary">
Comment ➤
</button>
</div>
</form>
);
【问题讨论】:
您甚至没有按照accepted answer of your own other question 中的说明进行操作...也就是说,您没有使用状态,而是直接改变了 DOM。 说得对。看到另一个问题。它也涵盖了这里所犯的大部分错误:) 你知道为什么有一个不受控制的组件会导致这个问题吗? 【参考方案1】:问题。
您将author
和message
存储在<CommentForm />
组件的实例变量(this.comment
) 上。即使在重新渲染后this.comment
仍保持相同的内存位置。所以即使在重新渲染之后,它也是同一个对象。您只是覆盖该对象的属性。您的父状态是一个 cmets 数组,其中每个元素只是指向同一 this.comment
对象的指针,每次都会被覆盖。
看看链接,继续添加 cmets 看看父组件的状态发生了什么 https://stackblitz.com/edit/react-cubmcf
解决方案
把handleSubmit修改成这样。
handleCommentSubmit = newComment =>
var comments = this.state.comments;
// var newComments = comments.concat([newComment]);
this.setState(
comments: [...comments, newComment]
);
;
更新评论表单以使用状态而不是实例变量。
所做的更改
-
直接删除了更新 dom 元素。
输入字段使用受控组件
一些常规重构。
class CommentForm extends React.Component
constructor(props)
super(props);
this.state = author: "", message: "" ;
handleSubmit = e =>
e.preventDefault();
const author, message = this.state;
//this stops any comment submittal if anything missing
if (!author || !message)
return;
this.props.onCommentSubmit( author, message );
this.setState(author: "", message : "")
;
handleFormChange = e =>
e.preventDefault();
this.setState([e.target.name]: e.target.value);
;
render()
const author, message = this.state;
return (
<form
className="ui form"
method="post"
onChange=e =>
this.handleFormChange(e);
onSubmit=e =>
this.handleSubmit(e);
>
<div className="form-group">
<input
className="form-control"
placeholder="user..."
name="author"
type="text"
value = author
/>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="comment..."
name="message"
value=message
/>
</div>
<div className="form-group">
<button disabled=null className="btn btn-primary">
Comment ➤
</button>
</div>
</form>
);
【讨论】:
你知道为什么实例变量会导致这个问题,但使用状态会解决它吗? 因为即使在重新渲染之后它们也会引用同一个对象。这就是为什么您在添加后看到重复的原因。我在这里使用了状态,并在提交评论后将状态重置为新对象 更新了答案以提供对该问题的解释。希望能帮助到你。如果觉得有用,请标记为正确/赞成 感谢您的回复。我仍然在努力理解 cmets 数组的每个元素如何指向同一个对象。当然,如果每次提交新表单时它都会收到newComment
s 道具,那么每个元素中应该有不同的条目吗?我发现当控制台在 this.comment
被传递之前记录它的值时,它包含一组当前评论的条目,而不是两组相同的评论。那么commentList
是如何复制评论的?
我将更新我的答案以更详细地涵盖您的代码的问题。需要一些时间。您可以稍后再回来查看。【参考方案2】:
this.setState(prevState => (comments: [...prevState.comments, newComment]))
【讨论】:
我在实施后仍然遇到同样的问题 如果您提供代码沙箱的示例会很好 虽然这可能是原始问题的解决方案,但您应该提供上下文并解释原因。以上是关于为啥创建评论部分时我的状态没有按预期更新?的主要内容,如果未能解决你的问题,请参考以下文章