当试图作为道具传递时,React状态变量意外未定义

React state variable unexpectedly undefined when trying to pass as props

本文关键字:React 状态 变量 未定义 意外      更新时间:2023-09-26

我有一个这样的React组件层次结构:

 AgreementContentWidget
   AgreementTitleContainer
     AgreementTitle <-- this is ok, but
     CommentList    <-- problem here
   SectionsContainer

mount AgreementContentWidget:

 <AgreementContentWidget agreement_id={85} />

请求小部件加载协议85。

var AgreementContentWidget = React.createClass({
  getInitialState: function() {
    return {agreement: []};
  },
  componentWillMount: function() {
    this.loadAgreementDetails();
  },
  loadAgreementDetails: function() {
    $.ajax({
      url: "/agreements/" + this.props.agreement_id, 
      type: 'GET',
      dataType: 'json',
      success: function(agreement) {
        this.setState({agreement: agreement.agreement});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error("/agreements/" + this.props.agreement_id, status, err.toString());
      }.bind(this)
    });
  },
  handleAgreementUpdate: function() {
    this.loadAgreementDetails();
  },
  render: function() {
    return (
      <div className="panel panel-default">
        <div className="panel-body" id={"container_agreement_" + this.state.agreement.id}>
          <div className="col-lg-12">           
            <AgreementTitleContainer agreement={this.state.agreement} onAgreementUpdate={this.handleAgreementUpdate} />
            <SectionsContainer agreement={this.state.agreement} />
          </div>
        </div>
      </div>
    );
  }
});

我将agreement={this.state.agreement}传递给<AgreementTitleContainer>以呈现包含协议标题的组件。

Inside <AgreementTitleContainer>:

var AgreementTitleContainer = React.createClass({
  handleAgreementUpdate: function() {
    this.props.onAgreementUpdate();
  },
  render: function() {
    return (
      <div className="row" id="container_title">
        <AgreementTitle agreement={this.props.agreement} onAgreementUpdate={this.handleAgreementUpdate} />
        <CommentList commentable_type={"agreement"} commentable_id={this.props.agreement.id} />
        <br />
      </div>
    );
  }
});

<AgreementTitle>{this.props.agreement},其内容为协议85。它按预期呈现并执行其他操作。然而,<CommentList>commentable_idundefined,因为this.props.agreement.id是未定义的(通过调试器:this.props.agreement对于该组件总是未定义的)。

:

  • 为什么{this.props.agreement}定义为<AgreementTitle> ?
  • 为什么<CommentList>没有定义?

我知道<CommentList>在其他上下文中按预期工作,其中值传递给commentable_id(例如commentable_id={42})而不是从this.state中的对象(如上所述)。

我注意到,对于<CommentList>,当它在loadComments()中尝试GET请求时:

var CommentList = React.createClass({
  getInitialState: function() {
    return {comments: []};
  },
  componentDidMount: function() {
    this.loadComments();
  },
  loadComments: function() {
    $.ajax({
      url: "/comments/?commentable_type=" + this.props.commentable_type + "&id=" + this.props.commentable_id, 
      type: 'GET',
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments.comments});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error("/comments/?commentable_type=" + this.props.commentable_type + "&id=" + this.props.commentable_id, status, err.toString());
      }.bind(this)
    });
  },
  ...
  ...
}

我从失败的GET得到一个错误消息

GET https://tinya-thedanielmay-2.c9.io/comments/?commentable_type=agreement&id=undefined 404 Not Found

我期望,因为id(从上面的this.props.commentable_id)是undefined。然而,console.error函数随后打印出:

/comments/?commentable_type=agreement&id=85 error Not Found

在执行时,this.props.commentable_id似乎已经被定义了。

我认为<AgreementContentWidget>的决定:

  • GET协议85
  • 设置this.state.agreement与85协议细节<——
  • 然后继续使用this.state.agreement作为道具的输入,例如agreement={this.state.agreement}

和我的问题有关。

在体系结构上,我希望将GET协议保留在<AgreementContentWidget>级别,并将检索到的协议细节向下传播。

我错过了什么?

你在getInitialState中初始化的是<AgreementContentWidget>第一次渲染时传递的内容。您认为<AgreementTitle>工作正常,因为当您从后端获得协议数据时,它会重新渲染,但是CommentsList依赖于该ID来加载自己的数据,在渲染之前会出错。它不知道要出去再次获取数据,因为它只在componentDidMount中加载。

您应该延迟检索注释,直到获得正确的属性。要做到这一点,使用React的componentWillReceiveProps(nextProps)函数,直到agreement.id存在,并且在加载ajax之前不为0。

所以从CommentList中删除this.componentDidMount并添加这个:

this.componentWillReceiveProps(nextProps){
  if(nextProps.agreement && nextProps.agreement.id){
    this.loadComments();
  }
}

您还应该设置propTypes,以便您可以通过良好的消息提醒这些问题,并为您的代码提供一些自我文档。