javascript - React state variable unexpectedly undefined when trying to pass as props - Stack Overflow

I have a React ponent hierarchy like this:AgreementContentWidgetAgreementTitleContainerAgreementTitle &

I have a React ponent hierarchy like this:

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

Mounting AgreementContentWidget:

 <AgreementContentWidget agreement_id={85} />

requests the widget to load agreement 85.

var AgreementContentWidget = React.createClass({
  getInitialState: function() {
    return {agreement: []};
  },

  ponentWillMount: 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>
    );
  }
});

I pass agreement={this.state.agreement} to <AgreementTitleContainer> to render ponents prising an agreement's title.

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 mentable_type={"agreement"} mentable_id={this.props.agreement.id} />
        <br />
      </div>
    );
  }
});

<AgreementTitle> takes {this.props.agreement} and it's got contents of agreement 85. It renders and does other operations as expected. However, <CommentList> has undefined for mentable_id as this.props.agreement.id is undefined (via debugger: this.props.agreement is always undefined for this ponent).

So:

  • Why is {this.props.agreement} defined for <AgreementTitle>?
  • Why is it undefined for <CommentList>?

I know that <CommentList> works as expected in other contexts where a value is passed to mentable_id (e.g. mentable_id={42}) rather than from an object in this.state (as above).

I noted that for <CommentList> when it tries a GET request in loadComments() below:

var CommentList = React.createClass({
  getInitialState: function() {
    return {ments: []};
  },

  ponentDidMount: function() {
    this.loadComments();
  },

  loadComments: function() {
    $.ajax({
      url: "/ments/?mentable_type=" + this.propsmentable_type + "&id=" + this.propsmentable_id, 
      type: 'GET',
      dataType: 'json',
      success: function(ments) {
        this.setState({ments: mentsments});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error("/ments/?mentable_type=" + this.propsmentable_type + "&id=" + this.propsmentable_id, status, err.toString());
      }.bind(this)
    });
  },
  ...
  ...
}

I get an error message from failed GET

GET /?mentable_type=agreement&id=undefined 404 Not Found

which I expect, as id (from this.propsmentable_id above) is undefined. However, the console.error function then prints out:

/ments/?mentable_type=agreement&id=85 error Not Found

So at that point of execution, this.propsmentable_id seems to be defined now.

I'm thinking that the decision for <AgreementContentWidget> to:

  • GET agreement 85
  • Set this.state.agreement with agreement 85 details <--
  • Then proceed to use this.state.agreement as input to props, e.g. agreement={this.state.agreement}

has something to do with my issues.

Architecturally, I'd like to retain the GET agreement at <AgreementContentWidget> level and have the retrieved agreement details propagate down.

What am I missing?

I have a React ponent hierarchy like this:

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

Mounting AgreementContentWidget:

 <AgreementContentWidget agreement_id={85} />

requests the widget to load agreement 85.

var AgreementContentWidget = React.createClass({
  getInitialState: function() {
    return {agreement: []};
  },

  ponentWillMount: 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>
    );
  }
});

I pass agreement={this.state.agreement} to <AgreementTitleContainer> to render ponents prising an agreement's title.

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 mentable_type={"agreement"} mentable_id={this.props.agreement.id} />
        <br />
      </div>
    );
  }
});

<AgreementTitle> takes {this.props.agreement} and it's got contents of agreement 85. It renders and does other operations as expected. However, <CommentList> has undefined for mentable_id as this.props.agreement.id is undefined (via debugger: this.props.agreement is always undefined for this ponent).

So:

  • Why is {this.props.agreement} defined for <AgreementTitle>?
  • Why is it undefined for <CommentList>?

I know that <CommentList> works as expected in other contexts where a value is passed to mentable_id (e.g. mentable_id={42}) rather than from an object in this.state (as above).

I noted that for <CommentList> when it tries a GET request in loadComments() below:

var CommentList = React.createClass({
  getInitialState: function() {
    return {ments: []};
  },

  ponentDidMount: function() {
    this.loadComments();
  },

  loadComments: function() {
    $.ajax({
      url: "/ments/?mentable_type=" + this.props.mentable_type + "&id=" + this.props.mentable_id, 
      type: 'GET',
      dataType: 'json',
      success: function(ments) {
        this.setState({ments: ments.ments});
      }.bind(this),
      error: function(xhr, status, err) {
        console.error("/ments/?mentable_type=" + this.props.mentable_type + "&id=" + this.props.mentable_id, status, err.toString());
      }.bind(this)
    });
  },
  ...
  ...
}

I get an error message from failed GET

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

which I expect, as id (from this.props.mentable_id above) is undefined. However, the console.error function then prints out:

/ments/?mentable_type=agreement&id=85 error Not Found

So at that point of execution, this.props.mentable_id seems to be defined now.

I'm thinking that the decision for <AgreementContentWidget> to:

  • GET agreement 85
  • Set this.state.agreement with agreement 85 details <--
  • Then proceed to use this.state.agreement as input to props, e.g. agreement={this.state.agreement}

has something to do with my issues.

Architecturally, I'd like to retain the GET agreement at <AgreementContentWidget> level and have the retrieved agreement details propagate down.

What am I missing?

Share Improve this question asked Jun 4, 2015 at 5:03 Daniel MayDaniel May 2,2625 gold badges27 silver badges44 bronze badges 1
  • 1 I don't think that this has anything to do with your problem, but the React docs say specifically to load ajax in ComponentDidMount as you're doing in CommentList. In AgreementWidget you're loading ajax in ComponentWillMount. – Jonathan Rowny Commented Jun 4, 2015 at 5:58
Add a ment  | 

1 Answer 1

Reset to default 4

What you're initializing in getInitialState is what's passed the first time <AgreementContentWidget> renders. You think <AgreementTitle> works properly because it is re-rendered when you get the agreement data from your backend, but CommentsList, being dependent on that ID to load its own data, will error out before it can render. It doesn't know to go out and get the data again because it's only loading in ponentDidMount.

You should defer the retrieval of ments until you get the correct property. To do this, use React's ponentWillReceiveProps(nextProps) function and wait until agreement.id exists, and is not 0 before loading your ajax.

So REMOVE this.ponentDidMount from CommentList and ADD this:

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

You should also set propTypes so you can be alerted to these issues with nice messages and provide some self documentation to your code.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744294509a4567194.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信