简体   繁体   中英

How do I delete specific child React components?

JSFiddle: https://jsfiddle.net/morahood/cp569zg6/38/

When I delete a child ServiceItem component from the ServiceList component, I am running into an issue where the last ServiceItem in the list is removed instead of the targeted ServiceItem . The JSFiddle link above will help understand the issue, just make sure you enter in some identifiable information in the input fields so you can see what is getting deleted.

How do I fix my application so that the respective component is deleted?

var ServiceForm = React.createClass({
  render: function() {
    return (
      <form onSubmit={ this.handleFormSubmission } >
        { this.renderServiceItemList() }
        <div className="btn btn-default btn-sm" onClick={ this.addServiceItem }>+ Append New Service Item</div>
        <button type="submit" className="btn btn-success btn-lg pull-right">Submit</button>
      </form>
    );  
  },

  getInitialState: function() {
    return ({
      serviceItems: [this.renderServiceItem]
    });
  },

  handleFormSubmission: function(event) {
    event.preventDefault();
    var data = this.refs.service_item_list.getAllServiceItems();
    var json = {
      "service_request" : {
        "services" : data,
        "additional_recipients" : 'test@example.com',
        "comments" : 'Hello world!'
      }
    };
    console.log(json);
  },


  addServiceItem: function(event) {
    var copy = this.state.serviceItems.slice();
    copy.push(this.renderServiceItem);

    this.setState({
      serviceItems: copy
    });
  },

  renderServiceItem: function(item, i) {
    return (
      <ServiceItem removeServiceItem={ this.removeServiceItem } data={item} key={i} ref={"service_item_" + i} />
    );
  },

  removeServiceItem: function(event) {
    var index = parseInt(event.target.value, 10);
    var copy = this.state.serviceItems.slice();
    copy.splice(index, 1);

    this.setState({
      serviceItems: copy
    });
  },

  renderServiceItemList: function() {
    return (
      <ServiceItemList serviceItems={ this.state.serviceItems }
                  renderServiceItem={ this.renderServiceItem }
                  removeServiceItem={ this.removeServiceItem }
                                ref="service_item_list" />
    );
  }
});





var ServiceItemList = React.createClass({
  render: function() {
    var content;
    if (this.props.serviceItems.length < 1) {
      content = <div className="empty-service-list">There are no service items, click on append new service item below!</div>;
    } else {
      content = this.props.serviceItems.map(this.props.renderServiceItem);
    }  


    return (
      <div>
        { content }
      </div>
    );
  },

  getAllServiceItems: function() {
    var data = [];

    for (var ref in this.refs) {
      data.push(this.refs[ref].serializeItem());
    }

    var merged = [].concat.apply([], data);
    return(merged);
  }
});





var ServiceItem = React.createClass({
  render: function() {
    return (
      <div className="row">
        <div className="col-sm-3">
          <div className="form-group service-item">

            <label>Service Item </label>
            <select multiple ref="service_types" className="form-control">
              <option>Oil Change</option>
              <option>Tire Rotation</option>
              <option>New Wiper Blades</option>
            </select>
          </div>
        </div>

        <div className="col-sm-3">
          <div className="form-group">
            <label>Customer </label>
            <select ref="customer" className="form-control">
              <option>Troy</option>
              <option>Dave</option>
              <option>Brandon</option>
            </select>
          </div>
        </div>        

        <div className="col-sm-3">
          <div className="form-group">
            <label>Manufacturer </label>
            <div className="input-group">
               <input ref="manufacturer" type="text" className="form-control" />
            </div>
          </div>
        </div>


        <div className="col-sm-3">
          <div className="form-group">
            <label>Model </label>
            <div className="input-group">
               <input ref="model" type="text" className="form-control" />
            </div>
            <a href="#" onClick={ this.props.removeServiceItem }>
                <span aria-hidden="true" className="remove-fields" onClick={ this.props.removeServiceItem }>&times;</span>
            </a>
          </div>
        </div>
      </div>
    );
  },

  getInitialState: function() {
    return({
      service_types: [],
      customer: '',
      manufacturer: '',
      model: ''
    });
  },

  serializeItem: function() {
    var customer = this.refs.customer.value;
    var manufacturer = this.refs.manufacturer.value;
    var model = this.refs.model.value;

    var selected = this.getSelectedServiceItems();

    var services = this.getSelectedServiceItems().map(function(item) {
        return (
          {
            "service" : {
              "service_type" : item,
              "customer" : customer,
              "manufacturer" : manufacturer,
              "model" : model
            }      
          }
        )
        });
    return(services);
  },

  getSelectedServiceItems: function() {
        var select = this.refs.service_types; 
        var values = [].filter.call(select.options, function (o) {
      return o.selected;
    }).map(function (o) {
      return o.value;
    });
    return(values);
  }
});





ReactDOM.render(
  <ServiceForm />,
  document.getElementById('container')
);

Your issue is with your key={i} .

React lists requires the key of an item to be unique to the item, regardless of the position of the item in the list . This allows react to do smart management of the list in case of updates.

React will NOT render the following update correctly:

From:                         To:
name:     key:                name:          key:
Bill      0                   Bill           0
Charlie   1                   Dave           1
Dave      2

Because react thinks that the Charlie record is unchanged (because the key is unchanged).

I would advise you to put some sort of ID from the service-item retrieved as key.

On name, eg

From:                         To:
name:     key:                name:          key:
Bill      Bill                Bill           Bill
Charlie   Charlie             Dave           Dave
Dave      Dave

React will render the update correctly in this case (because key is unique to the item).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM