admin管理员组

文章数量:1208153

I have a component that generates a table with rows of data (<tr> etc.) based on an array of data retrieved via an AJAX call. Everything works well for editing and adding the data, but I am unable to determine how to make a distinct copy of the array (with distinct copies of the contained objects - by val, not by ref) so that when I remove the specified row of data, the applicable row is removed from the table.

Currently, because the contained objects are by ref, even when I make a copy of the array, my table has the last row removed (even though the row index and data is all correctly referenced and deleted in my AJAX call).

handleRowDelete: function(rowIdx) {
     // Correct row 
     var row = this.state.data[rowIdx];

     // This makes a new array, but the contained objects are still by ref
     var rows = this.state.data.slice();

     // This contains the proper row that will be deleted. If this array is set to data, the table is updated to reflect just the one row - as expected.
     var throwout = rows.splice(rowIdx, 1);
     console.log(throwout);

     // Whether I set via the React.addons: 
     var newState = React.addons.update(this.state, {
         data: { $set: rows }
     });
     this.setState(newState);

     // Or just set the state again via this.setState(...)
     //this.setState({data: rows, add: false});

     // It always just removes the last row in the component render
     // Even though the proper row gets deleted following in AJAX call
     $.ajax({
     ...
},
...    

I understand React can't make a proper diff so the render is not triggered, so can you show me how this should be handled?

UPDATE. Relevant loop:

var Grid = React.createClass({
    propTypes: {
        data: React.PropTypes.array.isRequired,
        onCellChange: React.PropTypes.func.isRequired,
        onRowCommit: React.PropTypes.func.isRequired
    },
    render: function() {
        var rows = this.props.data.map(function(rowData, index) {
            return <Row key={index} 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}
              </tbody>
            </Table>  
        );
    }
});

I have a component that generates a table with rows of data (<tr> etc.) based on an array of data retrieved via an AJAX call. Everything works well for editing and adding the data, but I am unable to determine how to make a distinct copy of the array (with distinct copies of the contained objects - by val, not by ref) so that when I remove the specified row of data, the applicable row is removed from the table.

Currently, because the contained objects are by ref, even when I make a copy of the array, my table has the last row removed (even though the row index and data is all correctly referenced and deleted in my AJAX call).

handleRowDelete: function(rowIdx) {
     // Correct row 
     var row = this.state.data[rowIdx];

     // This makes a new array, but the contained objects are still by ref
     var rows = this.state.data.slice();

     // This contains the proper row that will be deleted. If this array is set to data, the table is updated to reflect just the one row - as expected.
     var throwout = rows.splice(rowIdx, 1);
     console.log(throwout);

     // Whether I set via the React.addons: 
     var newState = React.addons.update(this.state, {
         data: { $set: rows }
     });
     this.setState(newState);

     // Or just set the state again via this.setState(...)
     //this.setState({data: rows, add: false});

     // It always just removes the last row in the component render
     // Even though the proper row gets deleted following in AJAX call
     $.ajax({
     ...
},
...    

I understand React can't make a proper diff so the render is not triggered, so can you show me how this should be handled?

UPDATE. Relevant loop:

var Grid = React.createClass({
    propTypes: {
        data: React.PropTypes.array.isRequired,
        onCellChange: React.PropTypes.func.isRequired,
        onRowCommit: React.PropTypes.func.isRequired
    },
    render: function() {
        var rows = this.props.data.map(function(rowData, index) {
            return <Row key={index} 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}
              </tbody>
            </Table>  
        );
    }
});
Share Improve this question edited Mar 11, 2015 at 2:17 Ted asked Mar 10, 2015 at 22:45 TedTed 7,25110 gold badges52 silver badges76 bronze badges 6
  • Since you don't have the entire code shown, are you adding the special key attribute to each tr so that React knows what's changed properly? facebook.github.io/react/docs/… – WiredPrairie Commented Mar 11, 2015 at 0:47
  • Yes. I'll add to sample. As stated, row adding and editing has no issue (which wouldn't be the case if didn't set the key). – Ted Commented Mar 11, 2015 at 2:14
  • Perhaps I should be just hiding the deleted row instead of removing? I just read (what turns out to be right above what you referenced) that for "Stateful Children For most components, this is not a big deal. However, for stateful components that maintain data in this.state across render passes, this can be very problematic. In most cases, this can be sidestepped by hiding elements instead of destroying them" – Ted Commented Mar 11, 2015 at 2:20
  • 1 Your key isn't unique to the row. You're just using the index..., which means that if you remove something, the index has changed. – WiredPrairie Commented Mar 11, 2015 at 3:07
  • The key should be based off a property of the data which is unique for each row, e.g. an order ID number. – Mark Commented Mar 11, 2015 at 13:44
 |  Show 1 more comment

2 Answers 2

Reset to default 16

You'll need to make sure that the key value remains a constant for the lifetime of the object instance. As you have it coded, the key value is based on the index into the Array. If you remove one element from the Array, the indexes are updated, and so would be the keys. And, as a result, an object's key would change, and React will appear to not properly apply the new array changes (even when the underlying array has changed).

You'll need to either use a unique value from each object instance as the key or create one artificially (just assign a unique number to each object).

Use unique key is in Data Objects as below.

data = [
  {
    id: 0,
    contents: 'data 0',
  },
  {
    id: 3,
    contents: 'data 3',
  },
  {
    id: 4,
    contents: 'data 4',
  },
];


var rows = this.props.data.map(function(rowData, index) {
            return <Row key={rowData.id} data={rowData} onRowDelete={this.props.onRowDelete.bind(null, index)} />;
          }, this);

React do not re-render the component has the same key as one before.

本文标签: