【问题标题】: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。
  • 说得对。看到另一个问题。它也涵盖了这里所犯的大部分错误:)
  • 你知道为什么有一个不受控制的组件会导致这个问题吗?

标签: javascript reactjs state


【解决方案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]
 });
 
};

更新评论表单以使用状态而不是实例变量。

所做的更改

  1. 直接删除了更新 dom 元素。
  2. 输入字段使用受控组件
  3. 一些常规重构。

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]}))

【讨论】:

  • 我在实施后仍然遇到同样的问题
  • 如果您提供代码沙箱的示例会很好
  • 虽然这可能是原始问题的解决方案,但您应该提供上下文并解释原因。
猜你喜欢
  • 1970-01-01
  • 2017-01-25
  • 1970-01-01
  • 2011-12-14
  • 2021-08-04
  • 1970-01-01
  • 2016-07-21
  • 1970-01-01
  • 2016-08-30
相关资源
最近更新 更多