从 React 中的数组中删除项目时出现意外行为

IT技术 javascript reactjs
2021-04-30 19:10:28

我已经四处寻找答案,但我只需要一些人工帮助。

我有一个简单的表单,用户可以在其中添加其他字段。用户还可以删除不再相关的字段。

这是我遇到问题的删除字段。

这些字段是从组件状态中的数组呈现的,但是当我尝试从数组中删除一个项目时,它始终是数组中最后一个被删除的项目。

import React, { Component } from "react";

class Form_newPriority extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listName: "",
      rows: [{ id: 1, fieldValue: "" }],
    };
  }

  addrow = () => {
    const rows = [...this.state.rows];
    rows.push({
      id: rows.length + 1,
      fieldValue: "",
    });
    this.setState({ rows: rows });
  };

  removeRow = (idx) => {
    const rows = [...this.state.rows];
    rows.splice(idx, 1);
    this.setState({ rows: rows });
  };

  render() {
    return (
      <div>
        <input
          className="w-full bg-white border border-alice px-4 py-2 mb-1 h-12 rounded-lg"
          type="text"
        ></input>
        <h2>Items:</h2>
        {this.state.rows.map((row, idx) => {
          return (
            <React.Fragment>
              <input
                className="w-half bg-white border border-alice px-4 py-2 mb-4 mr-4  h-12 rounded-lg"
                key={idx}
                type="text"
                placeholder={idx}
              ></input>
              <button
                className="mb-4 text-red-700 inline-block"
                onClick={() => {
                  this.removeRow(idx);
                }}
              >
                Remove
              </button>
            </React.Fragment>
          );
        })}
        <button
          className="bg-alice hover:bg-gray-400 text-c_base text-sm font-bold py-1 px-2 rounded block mr-4"
          onClick={this.addrow}
        >
          Add row
        </button>
      </div>
    );
  }
}

export default Form_newPriority;

我也试过

rows.filter()

但这会产生相同的结果。

我对 JS 和 React 还很陌生,所以我在这里做了一些愚蠢的事情吗?

2个回答

它可能看起来像是删除了最后一个元素,因为您正在使用它index作为身份。

当您使用递增时,id它更有意义

例子

const { Component, useState, useEffect } = React;

class Form_newPriority extends Component {
  constructor(props) {
    super(props);
    this.state = {
      listName: "",
      counter: 1,
      rows: [{ id: 1, fieldValue: "" }],
    };
  }

  addrow = () => {
    const rows = [...this.state.rows];
    const counter = this.state.counter + 1;
    rows.push({
      id: counter,
      fieldValue: "",
    });
    this.setState({ counter, rows });
  };

  removeRow = (idx) => {
    const rows = [...this.state.rows];
    rows.splice(idx, 1);
    this.setState({ rows: rows });
  };

  render() {
    return (
      <div>
        <input
          className="w-full bg-white border border-alice px-4 py-2 mb-1 h-12 rounded-lg"
          type="text"
        ></input>
        <h2>Items:</h2>
        {this.state.rows.map((row, idx) => {
          return (
            <React.Fragment key={row.id}>
              <input
                className="w-half bg-white border border-alice px-4 py-2 mb-4 mr-4  h-12 rounded-lg" 
                type="text"
                placeholder={row.id}
              ></input>
              <button
                className="mb-4 text-red-700 inline-block"
                onClick={() => {
                  this.removeRow(idx);
                }}
              >
                Remove
              </button>
            </React.Fragment>
          );
        })}
        <button
          className="bg-alice hover:bg-gray-400 text-c_base text-sm font-bold py-1 px-2 rounded block mr-4"
          onClick={this.addrow}
        >
          Add row
        </button>
      </div>
    );
  }
}

ReactDOM.render(
    <Form_newPriority />,
    document.getElementById('root')
  );
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

这是一个非常常见的问题,有时很难解决。它与 react 如何管理动态元素有关,例如从map.

问题

您可能已经看到警告,keymap. 如果没有key给出,react 会自动使用数组元素索引这是问题的根源。

React 使用它key来跟踪哪个元素是哪个。因此,假设您有一个返回 3 个 div 的地图。React 会用它的标签来标记这些 div 中的每一个,key以便它知道将来需要更新什么。结果如下:

<div key={0} val='a' />
<div key={1} val='b' />
<div key={2} val='c' />

这适用于添加和更新。如果您更新 index 处的条目1,react 将通过标识它来更新第二个元素key

棘手的场景(就像您发现的那样)正在删除。

假设您删除了中间元素。结果数组现在将只包含 2 个位置。现在您的 JSX 将如下所示:

<div key={0} val='a' />
<div key={1} val='c' />

也许你已经看到发生了什么?您删除了中间元素,但是对于 React,您已经删除了最后一个元素并更新了第二个元素。为什么?因为key是基于索引的,而且索引发生了变化。React 将div使用 的键卸载2,并保留div使用key1第二个div没有被移除,而是用一个新的props重新渲染。

解决方案

根级别修复是您需要为key每个元素提供一个不会随时间改变的唯一元素。这的实现可能因您的用例而异。

例如,如果您的列表保证每个条目都有唯一的值,您可以使用它。这是最简单的解决方案,但并不总是可用。

我过去使用的一个解决方案是保持计数器处于每次添加元素时递增的状态。然后我使用计数器id为每个元素制作一个这样,如果一个元素被删除,id则不会改变。

一个警告

人们在面对这个问题时犯的一个常见错误是key使用辅助函数为每个元素生成一个完全随机的。这可能会阻止当前的问题,但这是一种非常糟糕的做法,可能会导致未来的错误。如果key每次渲染都更改,则元素将卸载并重新安装每个渲染。div像这样的简单示例中,这不会有任何副作用,但是如果您的元素是另一个具有状态的组件,它将失去其状态。如果您的元素是一个输入,它可能会在每次更新时失去焦点。这很诱人,但不要使用随机生成的数字作为key.