2015-03-11 5 views
5

Szenario:React JS this.props.data nicht für untergeordnete Komponente in getInitialState definiert, obwohl es in machen definiert ist (und in Mutter definiert)

  1. Wiederverwendung von Komponenten (<Row /> -><Cell />) in derselbe Großeltern-Wrapper (<ShippingTable /> -><Grid />) für die Code-Wiederverwendung.
  2. Zum einen, ich ein Array von Daten zuweisen und durchschleifen, um untergeordnete Komponente wieder zu verwenden (<Row /> -><Cell />).
  3. Für die zweite ist es nur ein Objekt (mit den gleichen Eigenschaften wie die Objekte des Arrays), die ich direkt im Render zuweisen (keine Notwendigkeit für this.props.data.map Schleife als bereits nur ein Objekt).

Problem:

  1. Für die Anordnung, alle Arbeiten wie erforderlich. this.props.data wird an Kinder weitergegeben, Zustand wird durch verschiedene Ereignisse aktualisiert und alles ist gut.
  2. Für das einzelne Objekt funktioniert jedoch alles bis <Row />. Obwohl this.props.data gültige Werte enthält und ordnungsgemäß der untergeordneten Komponente <Cell /> zugeordnet ist, ist sie in <Cell />s getInitialState unerklärlicherweise nicht definiert (oder auf die ursprünglichen Werte gesetzt, die in getInitialState eingestellt sind). obwohl es abgesehen von in einer Map-Schleife aufgerufen wird, ist es genau den gleichen Code und Daten, die in der gleichen Rendering für mein Array von Daten funktioniert. Außerdem
  3. , this.props.data in <Cell /> 's machen ist in der Tat vorhanden ist und genau, aber wegen des ausgefallenen setState in getInitialState wird this.state.data undefiniert (oder auf was auch immer Werte in <ShippingTable />' s getInitialState).
  4. Doppel außerdem, wenn ich erzwinge eine UI neu rendern (z. B. ändern Sie eine andere Eigenschaft des Staates in der Großeltern <ShippingTable />), alles funktioniert so, wie ich es in erster Linie erwarten würde.

Es ist, als ob getInitialState in <Cell /> für nur Objekt meines einzigen Prozentsatz vor einer erfolgreichen AJAX Bevölkerung von setState genannt zu werden, oder es wieder heißt nicht, wenn der Zustand, nachdem sie bevölkerte mit aktualisierten Daten von dem Server geändert wird.

Hier ist eine abgespeckte (aber noch lange) Version des eigentlichen Code:

UPDATE - Gelöst

nie vergessen, dass mit Reaktion, immer nach unten Requisiten senden, bis Ereignisse senden, und immer halten so weit wie möglich bis zur Spitze angeben. Die Verantwortlichkeiten für das Status-Tracking von <Cell /> und <Row /> wurden entfernt, indem sie in die ShippingTable zurückversetzt wurden (anstatt auf vom Eltern übergeordnete Requisiten zu verweisen), wo der Status tatsächlich verfolgt wird (und immer verfolgt werden sollte). Laut @ rallrall bin ich von der Strecke abgekommen und habe unerklärlicherweise gegen das Framework gearbeitet. Alles sehr klar im Nachhinein (obwohl der falsche Ansatz für das Array funktioniert hat, aber nicht für das Objekt, hat das Wasser getrübt - besonders, wenn das Objekt auf ein Array umgestellt wurde, funktionierte es auch).

var ShippingInput = React.createClass({ 
     getInitialState: function() { 
      return { value: this.props.value }; 
     }, 
     handleChange: function(event) { 
      var value = event.target.value; 
      ...conversion to decimal and validation 
      this.props.onChange(value); 
      this.setState({ value: value }); 
     }, 
     render: function() { 
      return (
       <Input type="text" placeholder="0.00" bsStyle={this.validationState()} addonBefore={addOnBefore} 
        value={this.state.value} label={this.props.label} ref="input" onChange={this.handleChange} 
        groupClassName={this.props.groupClassName} labelClassName={this.props.labelClassName} onKeyDown={this.props.onKeyDown} /> 
     ); 
     } 
    }); 

    var Cell = React.createClass({ 
     propTypes: { 
      data: React.PropTypes.number.isRequired, 
      onChange: React.PropTypes.func.isRequired, 
      onRowEdit: React.PropTypes.func.isRequired 
     }, 
     getInitialState: function() { 
      // At this point, this.props.data is undefined *only for <Cell data={this.props.percentages} /> in <Row /> even though that prop is not null there. 
      return {value: this.props.data}; 
     }, 
     handleChange: function(value) { 
      this.props.onChange(value); 
      this.setState({ value: value }); 
     }, 
     render: function() { 
      var edit = this.props.edit; 
      var showInput = edit ? 'group-class' : 'group-class hide'; 
      var showText = edit ? 'hide' : 'btn btn-default btn-sm'; 

      var val = this.props.isRates ? accounting.formatMoney(this.state.value) : this.state.value; 

      // {this.state.value} is undefined here for only the percentages object 
      // {this.props.data} is *not undefined* 
      var input = <ShippingInput type="text" label={this.props.label} value={this.state.value} ref="input" 
        isRates={this.props.isRates} groupClassName={showInput} labelClassName="label-class sr-only" onKeyDown={this.handleKeyDown} onChange={this.handleChange} />; 

      var text = (<a href="#" className={showText} onClick={this.handleClick}>{val}</a>); 

      return (<td>{input}{text}</td>); 
     } 
    }); 

    var Row = React.createClass({ 
     propTypes: { 
      data: React.PropTypes.object.isRequired, 
      onCellChange: React.PropTypes.func.isRequired, 
      onRowCommit: React.PropTypes.func.isRequired 
     }, 
     getInitialState: function() { 
      return {edit: false}; 
     }, 
     handleChange: function(prop, val) { 
      this.props.onCellChange(prop, val); 
     }, 
     ... 
     render: function() { 
      var edit = this.state.edit; 
      var text = edit ? 'fa fa-save fa-fw' : 'fa fa-edit fa-fw'; 
      return <tr> 
       <Cell data={this.props.data.Canada} isRates={this.props.isRates} label="Canada" edit={edit} onRowEdit={this.handleRowEdit} onRowCommit={this.handleRowCommit} onChange={this.handleChange.bind(null, "Canada")} /> 
       <Cell data={this.props.data.Us} isRates={this.props.isRates} label="United States" edit={edit} onRowEdit={this.handleRowEdit} onRowCommit={this.handleRowCommit} onChange={this.handleChange.bind(null, "Us")} /> 
       <Cell data={this.props.data.International} isRates={this.props.isRates} label="International" edit={edit} onRowEdit={this.handleRowEdit} onRowCommit={this.handleRowCommit} onChange={this.handleChange.bind(null, "International")} /> 
       <td> 
        <Button href="#" ref="commit" onClick={this.handleRowCommit} bsStyle="primary" bsSize="small"><span className={text}></span></Button> 
       </td> 
      </tr>; 
     } 
    }); 

    var Grid = React.createClass({ 
     propTypes: { 
      data: React.PropTypes.array.isRequired, 
      percentages: React.PropTypes.object.isRequired, 
      onCellChange: React.PropTypes.func.isRequired, 
      onRowCommit: React.PropTypes.func.isRequired 
     }, 
     render: function() { 
      var rows = this.props.data.map(function(rowData, index) { 
       var id = rowData["Id"]; 
       return <Row key={id} isRates={true} data={rowData} onCellChange={this.props.onCellChange.bind(null, index)} onRowCommit={this.props.onRowCommit.bind(null, index)} onRowDelete={this.props.onRowDelete.bind(null, index)} />; 
      }, this); 

      return (
       <Table striped bordered hover responsive> 
        <thead> 
        <tr> 
        <th className="col-sm-4">Order Subtotal (up to)</th> 
        <th className="col-sm-2">Canada</th> 
        <th className="col-sm-2">US</th> 
        <th className="col-sm-2">International</th> 
        <th className="col-sm-1"></th> 
        </tr> 
        </thead> 
        <tbody> 
         {rows} 
         <tr><td colSpan="5">If the order subtotal is greater than the largest amount on the above chart, the following rates apply:</td></tr> 
         <Row key="Percentages" isRates={false} data={this.props.percentages} onCellChange={this.props.onPercentCellChange} onRowCommit={this.props.onPercentRowCommit} /> 
        </tbody> 
       </Table> 

      ); 
     } 
    }); 

    var ShippingTable = React.createClass({ 
     getInitialState: function() { 
      return this.props.initialData; 
     }, 
     loadFromServer: function() { 
      $.getJSON(this.props.url, function(data) { 
       if (!data || data.Success === false) { 
        toastr.error('Error loading shipping costs. Please refresh the page and try again.'); 
       } else if (this.isMounted()) { 
        // This change is not reflected in Row/Cell for this.state/props.percentages until after force a UI update (via handleAdd and handleCancel 
        // where this.state.add changes) even though a) percentages (a single object) holds valid values here and does all the way down to <Row /> 
        // and b) there is no similar issue with this.state.data. 
        this.setState({ data: data.Value.ShippingCostMatrix, percentages: data.Value.ShippingPercentage }); 
       } 
      }.bind(this)); 
     }, 
     componentDidMount: function() { 
      this.loadFromServer(); 
     }, 
     handleCellChange: function(rowIdx, prop, val) { 
      var row = copy(this.state.data[rowIdx]); 
      row[prop] = val; 
      var rows = this.state.data.slice(); 
      rows[rowIdx] = row; 
      rows.sort(sortBySubtotal); 
      this.setState({data: rows}); 
     }, 
     handlePercentCellChange: function(prop, val) { 
      var row = copy(this.state.percentages); 
      row[prop] = val; 
      this.setState({percentages: row}); 
     }, 
     handleAdd: function(event) { 
      event.preventDefault(); 
      this.setState({ add: true}); 
     }, 
     handleAddCancel: function(event) { 
      event.preventDefault(); 
      this.setState({ add: false}); 
     }, 
     render: function() { 
      var ctrl; 
      if (this.state.add) { 
       ctrl = (<NewRow onAddCancel={this.handleAddCancel} onRowAdd={this.handleRowAdd} />); 
      } 
      else { 
       ctrl = (
        <div> 
        <p><a onClick={this.handleAdd} className="btn btn-primary btn-lg">Add</a></p> 
        <Grid data={this.state.data} percentages={this.state.percentages} 
         onCellChange={this.handleCellChange} onPercentCellChange={this.handlePercentCellChange} onRowCommit={this.handleRowCommit} onPercentRowCommit={this.handlePercentRowCommit} onRowDelete={this.handleRowDelete} /> 
        </div> 
       ); 
      } 

      return <div>{ctrl}</div>; 
     } 
    }); 

    //React.render(<ShippingTable initialData={ {data : [], percentages: { Canada: 1, Us: 1.25, International: 2.25, Id: 1 }, add: false} } 
    React.render(<ShippingTable initialData={ {data : [], percentages : {}, add: false} } 
     url="/admin/shipping/costs" update="/admin/shipping/update" create="/admin/shipping/create" delete="/admin/shipping/delete" updatePercentage="/admin/shipping/updatepercentage" />, document.getElementById('shipTable')); 

Antwort

7

getInitialState soll den ursprünglichen Komponentenstatus unabhängig von den Requisiten zurückgeben.Wenn Sie in diesem Status wirklich einen Prop-Wert festlegen möchten, sollten Sie den Hook componentWillMount verwenden. Siehe docs.

Obwohl es scheint, dass Sie hier gegen den Rahmen arbeiten. Wenn Sie einen Prop-Wert ändern möchten, sollte die übergeordnete Komponente darauf reagieren und neue Props für die untergeordnete Komponente bereitstellen.

+2

Nun, duh. Vielen Dank. Erstaunlich, wie viel wieder klar wird, wenn man einen Tag später wiederkommt. Stützen Sie sich, Ereignisse auf und behandeln Sie den Zustand so nah wie möglich an der Spitze. Danke für die "Es scheint, als ob du hier gegen den Rahmen arbeitest." Ein schlechtes React-Beispiel hat mich auf den Pfad des Denkens gebracht, dass OnChange eines Inputs benötigt wird, um seinen eigenen Status zu verfolgen ... – Ted

Verwandte Themen