Sam Davies
Sam Davies

Reputation: 145

ReactJS - array in this.state is updated correctly on delete but result isn't rendered until later state changes?

I've seen variations of this question posted but none match my problem. I am displaying an array of text fields and want to be able to delete one of them. When the delete button next to any text field is clicked, the last item in the array disappears. However, I've checked this.state and the correct item was deleted, just the wrong one was taken from the rendered array. When I click a button that hides the text fields and then un-hide, the array is fixed and shows the correct item deleted. Why is this happening? The state is updated with what I want removed, but it seems like it renders something other than the state. I've tried using this.forceUpload(); in the setState callback but that does not fix it.

I've included some relevant snippets from the code and can include more if needed.

export class Questions extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      ...
      choices: []
      ...
    };
  }

  deleteChoice = (index) => {
    let tempChoices = Object.assign([], this.state.choices);
    tempChoices.splice(index, 1);
    this.setState({choices: tempChoices});
  };

  renderChoices = () => {
    return Array.from(this.state.choices).map((item, index) => {
        return (
          <li key={index}>
            <TextField defaultValue={item.text} style={textFieldStyle} hintText="Enter possible response..."
                       onChange={(event) => { this.handleChoice(event, index); }}/>
            <i className="fa fa-times" onClick={() => { this.deleteChoice(index); }}/>
          </li>
        );
    });
  };

  render() {
    let choices = (
      <div>
        <div className="choices">Choices</div>
        <ul>
          {this.renderChoices()}
          <li>
            <RaisedButton label="Add another" style={textFieldStyle} secondary
                        onClick={() => { this.addChoice(); }}/>
          </li>
        </ul>
      </div>
    );
    return (
      {choices}
    );
  }
}

Thanks for any help, this is wicked frustrating.

Upvotes: 1

Views: 83

Answers (1)

Jim Skerritt
Jim Skerritt

Reputation: 4596

You need to use a key other than the array index in your renderChoices function. You can read more about why this is the case in React's docs:

https://facebook.github.io/react/docs/multiple-components.html#dynamic-children

When React reconciles the keyed children, it will ensure that any child with key will be reordered (instead of clobbered) or destroyed (instead of reused).

Consider using item.text as the key or some other identifier specific to that item.

Upvotes: 2

Related Questions