Salman Djingueinabaye
Salman Djingueinabaye

Reputation: 87

Dynamically mapped components from array in state are not updating even after state changes

I'm mapping an array in the state to build dropdowns dynamically. I have two working methods to add and remove values in the array. When I remove and add values to the array I want the UI elements to also update as the state changes. Addition works but deletion fails every time. I have confirmed the array in the state is being changed correctly but I can only ever delete the newest component instead of a specific one. Thanks for your help.

 //
 handleToggle() {
    return this.state.AdditionQueryArray.map((array, index) => {
      let key = array.key;
      return (
        <div>
          <Select
            onChange={(e) => this.AdditionalOperatorHandleChange(e, key)}
            options={this.state.OperatorOptions}
            placeholder="Select Operator"
            menuPortalTarget={document.body}
            menuPosition={"fixed"}
          />
          <button onClick={() => this.deleteAdditonalQuery(index)}>
            Delete
          </button>
          {this.forceUpdate}
        </div>
      );
    });
  }

  createAdditionalQuery() {
    const state = this.state;

    let key = Math.random().toString(36).substring(7);

    var copyArr = [...this.state.AdditionQueryArray];

    copyArr.push({
      key: key,
      field: "fieldDefault",
      property: "propertyDefault",
      PropValue: "Propvalue",
    });
    this.setState({
      AdditionQueryArray: copyArr,
    });
  }

  deleteAdditonalQuery(indexVal) {
    var state = this.state;

    var copyArr = [...this.state.AdditionQueryArray];

    if (indexVal > -1) {
      copyArr.splice(indexVal, 1);
    }

    this.setState({
      AdditionQueryArray: copyArr,
    });
  }

  render() {
    return (
      <div>
        <button onClick={this.createAdditionalQuery}>Add Query </button>
        {this.handleToggle()}
      </div>
    );
  }
  ////

Upvotes: 0

Views: 392

Answers (1)

Drew Reese
Drew Reese

Reputation: 202638

Shallow copy and array.splice is interesting way to remove an element from an array by index. I suggest using array.filter instead, it's much more clean. I suggest always using functional state updates when updated arrays stored in state, especially when used in callbacks attached in loops. This avoids any stale state enclosures.

deleteAdditonalQuery(indexVal) {
  this.setState(prevState => ({
    AdditionQueryArray: prevState.AdditionQueryArray.filter(
      (el, index) => index !== indexVal
    ),
  }));
}

Similarly in the add handler, use a functional state update.

createAdditionalQuery() {
  this.setState(prevState => ({
    AdditionQueryArray: prevState.AdditionQueryArray.concat({
      key: Math.random().toString(36).substring(7),
      field: "fieldDefault",
      property: "propertyDefault",
      PropValue: "Propvalue",
    }),
  }));
}

In handleToggle remove the "dead" force update code, it shouldn't be necessary. It's not being invoked anyway. Don't forget to add a React key to the outermost element being mapped.

handleToggle() {
  return this.state.AdditionQueryArray.map((array, index) => {
    const key = array.key;
    return (
      <div key={key}> // <-- add React key!
        <Select
          onChange={(e) => this.AdditionalOperatorHandleChange(e, key)}
          options={this.state.OperatorOptions}
          placeholder="Select Operator"
          menuPortalTarget={document.body}
          menuPosition={"fixed"}
        />
        <button onClick={() => this.deleteAdditonalQuery(index)}>
          Delete
        </button>
      </div>
    );
  });
}

Upvotes: 1

Related Questions