ReactJS 删除通过计数创建的子元素

ReactJS remove child element created through count

我有两个组件,一个 TrackSection(父元素),它有一个按钮,每次单击它时都会创建一个 TrackItem(子元素)。子元素是通过变量 numTracks 构建的,每次单击按钮时该变量都会递增。添加按钮工作正常,但我在从数组中删除 TrackItem 时遇到问题。我尝试直接引用 track_items 但它不允许我这样做。

我对 React 和前端开发还很陌生。任何其他提示将不胜感激!

TrackSection.js

class TrackSection extends Component {
  constructor(props) {
    super(props);
    this.state = {
      numTracks: 0,
    };
  }

  onAddTrack = () => {
    this.setState({
      numTracks: this.state.numTracks + 1,
    });
  };

  onDeleteTrack = () =>{
   //????
  };


  render() {
    const track_items = [];
    for (var i = 0; i < this.state.numTracks; i += 1) {
      track_items.push(<TrackItem key={i} id={i} onDeleteTrack = {this.onDeleteTrack(i)}/>);
    }

    return (
      <div>
        <Button onClick={this.onAddTrack}>
            +new track
        </Button>
        {track_items}
      </div>
    );
  }
}

TrackItem.js

class TrackItem extends Component{
    constructor(props){
        super(props);
        this.state = {
            id: this.props.id,
            name: '',
        }
    }

    render(){
    var onDeleteTrack = this.props.onDeleteTrack
    return(
        <Grid container direction="row">
            <Grid item direction="column">
            //Dummy 
            </Grid>
            <button onClick={() => onDeleteTrack(this.props.id)}>Delete</button>
        </Grid>
        );
}}

小心不要在渲染函数中做太多逻辑,因为您当前的解决方案会在您每次添加新项目时重新创建所有 TrackItem。所以 React 不能做优化魔法。

第二点,现在你只有一个计数器,所以删除中间的一个元素可能不会产生你想要的效果。我假设轨道项目将有一些数据给他们。像名称等。因此只需将这些值存储在状态中并呈现每个项目。

这是一个示例解决方案,请根据您的需要进行修改:

class TrackSection extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tracks: []
    };
  }

  onAddTrack = () => {
    // Probably not the best way to create a id
    const randomId = Math.random().toString();

    const newTrack = {
      id: randomId,
      name: "Some name" + randomId
    };

    const newTracks = [
      // the tracks we allready have added
      ...this.state.tracks,
      // add a new track to the end
      newTrack
    ];

    // Replace state
    this.setState({
      tracks: newTracks
    });
  };

  onDeleteTrack = (id) => {
    // Keeps all tracks that don't match 'id'
    const tracksWithOutDeleted = this.state.tracks.filter(
      (track) => track.id !== id
    );

    // Replace the tracks, so now its gone!
    this.setState({
      tracks: tracksWithOutDeleted
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.onAddTrack}>+new track</button>
        {
          // Loop over tracks we have in state and render them
          this.state.tracks.map((track) => {
            return (
              <TrackItem
                id={track.id}
                name={track.name}
                onDeleteTrack={this.onDeleteTrack}
              />
            );
          })
        }
      </div>
    );
  }
}

和TrackItem.js:

class TrackItem extends Component {
  render() {
    const { onDeleteTrack, id, name } = this.props;
    return (
      <>
        <button onClick={() => onDeleteTrack(id)}>Delete {name}</button>
      </>
    );
  }
}

问题

您正在使用数组索引作为 React 键,并且 id。当您从数组中删除一个元素时,您可以将其从数组中删除,但由于项目向上移动以填充“洞”,现在数组中的所有元素都具有不正确的“id”/索引值。

解决方案

不要使用映射数组索引作为 React 键。

示例解决方案像以前一样使用递增的 id,但也将数组存储在状态中。这允许您一致地增加 id 键 and 为每个元素保留一个静态 id。

class TrackItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      id: this.props.id,
      name: ""
    };
  }

  render() {
    var onDeleteTrack = this.props.onDeleteTrack;
    return (
      <Grid container direction="row">
          <Grid item direction="column">
          //Dummy
          </Grid>
      <button onClick={() => onDeleteTrack(this.props.id)}>Delete {this.props.id}</button>
      </Grid>
    );
  }
}

class TrackSection extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tracks: [],
      id: 0,
    };
  }

  onAddTrack = () => {
    this.setState(prevState => ({
      tracks: [...prevState.tracks, prevState.id],
      id: prevState.id + 1,
    }));
  };

  onDeleteTrack = (id) =>{
   this.setState(prevState => ({
     tracks: prevState.tracks.filter(el => el !== id)
   }))
  };


  render() {
    return (
      <div>
        <button onClick={this.onAddTrack}>
            +new track
        </button>
        {this.state.tracks.map(track => (
          <TrackItem key={track} id={track} onDeleteTrack = {this.onDeleteTrack}/>
        ))}
      </div>
    );
  }
}