简体   繁体   中英

React rendering deleted array element after setState()

I'm having an issue where React seems to be rendering a deleted array element after I've removed it from an array in the components state. After setState(), rendering is triggered, but the deleted item is shown, instead of the remaining item (see GIF video below).

Although the component is extremely simple and I've spent hours on this problem, I haven't been able to solve this issue. The strange thing is that the newState object actually contains the valid new list, but it's not rendered.

I really hope someone can help me figure this out!

GIF视频显示问题

import React from "react";
import Button from "@material-ui/core/Button";
import update from "immutability-helper";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import * as R from "ramda";

class SessionNoteGroup extends React.Component {
  state = {
    id: this.props.id,
    notes: this.props.notes
  };

  render() {
    const { classes } = this.props;

    const { id, notes } = this.state;

    return (
      <div>
        <Grid container>
          <Grid item xs={12}>
            <TextField
              multiline
              fullWidth
              id="notes"
              name="notes"
              label="Notes"
              rows="2"
              value={notes}
              onChange={this.handleValueChange}
            />
          </Grid>
        </Grid>
        <Button variant="outlined" color="primary" onClick={this.handleDelete}>
          Delete
        </Button>
      </div>
    );
  }

  handleValueChange = event => {
    const { name, value } = event.target;
    const { id, notes } = this.state;

    let newState = {
      id: id,
      notes: value
    };

    this.setState(newState);
  };

  handleDelete = () => {
    this.props.onDelete(this.state.id);
  };
}

class SessionNotes extends React.Component {
  state = {
    notes: this.props.notes.slice(),
    deleted: []
  };

  next_id = 2;

  createNotes = () => {
    let notesList = [];
    for (let i = 0; i < this.state.notes.length; i++) {
      const { id, notes } = this.state.notes[i];
      notesList.push(
        <SessionNoteGroup
          id={id}
          notes={notes}
          onDelete={this.handleDelete}
          index={i + 1}
        />
      );
    }
    console.log(notesList);
    return notesList;
  };

  handleDelete = id => {
    const newNotes = R.filter(note => note.id !== id, this.state.notes);
    this.setState({ notes: newNotes });
  };

  handleClickAdd = async () => {
    const note = {
      id: this.next_id,
      notes: ""
    };
    this.next_id++;
    const newState = update(this.state, { notes: { $push: [note] } });
    this.setState(newState);
  };

  render() {
    return (
      <div>
        {this.createNotes()}
        <Button
          variant="outlined"
          color="primary"
          onClick={this.handleClickAdd}
        >
          Add
        </Button>
      </div>
    );
  }
}

export default SessionNotes;

Few things.

When you want to set the state, based on the prev state, use the setState with a callback, which takes as argument the prevState. So the next code:

handleValueChange = event => {
    const { name, value } = event.target;
    const { id, notes } = this.state;

    let newState = {
      id: id,
      notes: value
    };

    this.setState(newState);
  };

Will be something like:

handleValueChange = event => {
    const { name, value } = event.target;
    const { id, notes } = this.state;
    this.setState(prevState => ({ id: prevState.id, notes: value}));
  };

Same in the below component:

handleDelete = id => {
    const newNotes = ;
    this.setState(prevState => ({ notes: R.filter(note => note.id !== id, prevState.notes) }));
  };

And so on for all the times that you update the state based on previous state value.

Then when you do create a list of elements in react, use key property:

<SessionNoteGroup
          key={id}
          id={id}
          notes={notes}
          onDelete={this.handleDelete}
          index={i + 1}
        />

That's used by react for managing the render of list of items

Try adding a key to the container div of your render

return (
      <div key = {this.props.id}>
        <Grid container>
          <Grid item xs={12}>
            <TextField
              multiline
              fullWidth
              id="notes"
              name="notes"
              label="Notes"
              rows="2"
              value={notes}
              onChange={this.handleValueChange}
            />
          </Grid>
        </Grid>
        <Button variant="outlined" color="primary" onClick={this.handleDelete}>
          Delete
        </Button>
      </div>
    );

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