为啥创建评论部分时我的状态没有按预期更新?

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 &#10148;
            </button>
          </div>
        </form>

    );
  

【问题讨论】:

您甚至没有按照accepted answer of your own other question 中的说明进行操作...也就是说,您没有使用状态,而是直接改变了 DOM。 说得对。看到另一个问题。它也涵盖了这里所犯的大部分错误:) 你知道为什么有一个不受控制的组件会导致这个问题吗? 【参考方案1】:

问题

您将authormessage 存储在&lt;CommentForm /&gt; 组件的实例变量(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 &#10148;
          </button>
        </div>
      </form>
    );
  

【讨论】:

你知道为什么实例变量会导致这个问题,但使用状态会解决它吗? 因为即使在重新渲染之后它们也会引用同一个对象。这就是为什么您在添加后看到重复的原因。我在这里使用了状态,并在提交评论后将状态重置为新对象 更新了答案以提供对该问题的解释。希望能帮助到你。如果觉得有用,请标记为正确/赞成 感谢您的回复。我仍然在努力理解 cmets 数组的每个元素如何指向同一个对象。当然,如果每次提交新表单时它都会收到newComments 道具,那么每个元素中应该有不同的条目吗?我发现当控制台在 this.comment 被传递之前记录它的值时,它包含一组当前评论的条目,而不是两组相同的评论。那么commentList 是如何复制评论的? 我将更新我的答案以更详细地涵盖您的代码的问题。需要一些时间。您可以稍后再回来查看。【参考方案2】:
this.setState(prevState => (comments: [...prevState.comments, newComment]))

【讨论】:

我在实施后仍然遇到同样的问题 如果您提供代码沙箱的示例会很好 虽然这可能是原始问题的解决方案,但您应该提供上下文并解释原因。

以上是关于为啥创建评论部分时我的状态没有按预期更新?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的图片框动画没有按预期工作(C# Windows 窗体应用程序)?

为啥我的替换没有按预期工作[重复]

为啥我的查询没有按预期工作?

为啥我的依赖属性绑定没有按预期工作?

为啥我的 javascript 代码没有按预期工作

为啥我的 scss 变量没有按预期解析? [复制]