Удаление элемента из объекта состояния в реакции (странный рендеринг) - PullRequest
0 голосов
/ 18 января 2019

Я создал проект в Reactjs, где у меня есть список имен, которые я хочу отобразить в пользовательском списке. Каждый элемент имеет кнопку для удаления элемента, однако всякий раз, когда я нажимаю кнопку, последний элемент удаляется из списка независимо от того, какой элемент списка я нажимаю.

Я уже пытался отлаживать свой код с помощью js-консоли, но это сделало проблему еще более странной, поскольку консоль отображает правильное состояние, когда компонент «Список» отображает элемент списка, которого больше нет в объекте состояния

import React, { Component } from 'react';
import './ListItem'
import ListItem from './ListItem';

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            items: [
                {name: 'Tobi'},
                {name: 'Maxi'},
                {name: 'David'},
                {name: 'Peter'},
            ]
        }
    }

    removeItem = (id) => {
        let few = this.state.items;
        few.splice(id,1);
        //console.log(this.state.items);   
        this.setState({items: few}, function(){
            console.log(this.state.items.map((item) => item.name));
            this.forceUpdate();
        });
    }

    render() { 
        return (
            <div>
                <ul>
                    {this.state.items.map((item, i) => <ListItem name={item.name} key={i} id={i} remove={this.removeItem}/>)}
                </ul>
            </div>
        );
    }
}

import React, { Component } from 'react';

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

    test = () => {
        this.props.remove(this.state.id);
    }

    render() { 
        return (
            <li>{this.state.name} <button onClick={() => this.test()}>click me</button></li>
        );
    }
}

export default ListItem;

Как уже говорилось, я ожидал, что нужный элемент списка будет удален, однако это всегда последний элемент, который больше не отображается, даже если объект состояния говорит иначе.

1 Ответ

0 голосов
/ 18 января 2019

Основная проблема в том, что вы используете индекс массива как key. При первом рендеринге ListItems у вас есть:

  • Имя ListItem = {'Tobi'} ключ = {0}
  • Имя ListItem = {'Maxi'} ключ = {1}
  • Имя ListItem = {'David'} ключ = {2}
  • Имя ListItem = {'Peter'} ключ = {3}

Допустим, вы удалили элемент с индексом 1, все остальные элементы сместятся в индекс:

  • Имя ListItem = {'Tobi'} ключ = {0}
  • ListItem name = {'David'} key = {1}
  • ListItem name = {'Peter'} key = {2}

React будет сравнивать только ключи, и поскольку единственное различие между первым и вторым рендером состоит в том, что элемент с ключом = {3} отсутствует, это элемент, который будет удален из домена.

Также избегайте прямого изменения состояния (few.splice(id,1)) и старайтесь избегать this.forceUpdate()

Попробуйте использовать фактический идентификатор в ваших данных:

import React, { Component } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

class List extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [
        { id: 1, name: "Tobi" },
        { id: 2, name: "Maxi" },
        { id: 3, name: "David" },
        { id: 4, name: "Peter" }
      ]
    };
  }

  removeItem = id => {
    let few = this.state.items.filter(item => item.id !==id);
    //console.log(this.state.items);
    this.setState({ items: few }, function() {
      console.log(this.state.items.map(item => item.name));
      //this.forceUpdate();
    });
  };

  render() {
    return (
      <div>
        <ul>
          {this.state.items.map((item, i) => (
            <ListItem
              name={item.name}
              key={item.id}
              id={item.id}
              remove={this.removeItem}
            />
          ))}
        </ul>
      </div>
    );
  }
}

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

  test = () => {
    this.props.remove(this.state.id);
  };

  render() {
    return (
      <li>
        {this.state.name} <button onClick={() => this.test()}>click me</button>
      </li>
    );
  }
}


function App() {
  return (
    <div className="App">
      <List />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
...