【问题标题】:Delete a row from array by component event in that array using React使用 React 通过该数组中的组件事件从数组中删除一行
【发布时间】:2021-07-29 16:23:06
【问题描述】:

以下是我遇到的问题的基本示例。

我有一个数组中的项目列表。用户可以将另一个项目添加到列表中,并显示在页面上。每个项目都有一个删除按钮,删除按钮是数组项目内的一个组件(这样每个项目都可以有一个按钮来执行不同的操作。在我的示例中,操作是删除,但是稍后可能是“发送”或“删除”或“取消”或“编辑”...)

问题是,当我在这种情况下单击“操作”删除时,我想知道这是数组中的哪个项目。这样,我可以获取数组索引并删除它。或者稍后从

import React, {useState} from "react"

function App() {

  const [list, setList] = useState([])

  function addRow(){
    let newRow = {
      name: "Test",
      action: <span onClick={e=>removeThisRow()}>REMOVE</span>
    }

    setList([
      ...list,
      newRow
    ])
  }

  function removeThisRow(){
    // need this to remove the specific item from my list array...
    console.log("removing...")
  }

  return (
    <div>
      {
        list.map(item=>(
          <div>
            {item.name} | {item.action}
          </div>
        ))
      }

      <div onClick={e=>addRow()}>ADD ROW</div>
    </div>
  );
}

export default App;

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    工作和测试

    function App() {
    
      const [list, setList] = React.useState([])
    
      function addRow(){
        let newRow = {
            id: Math.random(),
          name: `Test-${Math.random()}`,
          action: (id) => <span onClick={()=>removeThisRow(id)}>REMOVE</span>
        }
    
        setList([
          ...list,
          newRow
        ])
      }
    
      function removeThisRow(id){
        setList(l => l.filter(li => li.id !== id))
      }
    
      return (
        <div>
          {
            list.map((item)=>(
              <div key={item.id}>
                {item.name} | {item.action(item.id)}
              </div>
            ))
          }
    
          <div onClick={e=>addRow()}>ADD ROW</div>
        </div>
      );
    }
    

    Id 只是随机数,我会使用更独特的东西,例如 uuid()

    【讨论】:

    • 很好的建议,实际上这是我最初尝试过的...但是,您最终遇到了同样的问题,并且在您的 removeThisRow 函数中,列表不知道该项目的索引呢。如果你在那里控制你的“列表”,你会看到它总是落后一个项目。
    • 我不太明白你的意思。函数在定义时不知道索引,但它在单击时将其作为参数,因此索引是正确的。如果您在 setList 之后立即记录,列表的日志将落后,因为这是异步的。
    • 用 id 更新了我的示例,它在这里工作是小提琴jsfiddle.net/6qdf7pc2
    【解决方案2】:

    这是从数组中动态添加/编辑/删除项目的另一种方法。无需在数组中创建不必要的 JSX,您可以映射数组列表并根据唯一的 id 编辑/删除行。

    添加新行之前,您可以通过添加两个额外的输入切换来变得更加复杂。这两个切换可能会在创建行时将isEditableisRemovable 属性添加到行。然后,这些属性可用于在显示列表时动态包含或排除按钮。这是一种更简洁的方法,因为您不会为每一行反复重新创建相同的按钮,而是足够灵活以有条件地呈现它们。

    在相关说明中,使用数组索引作为key is anti-pattern

    演示源代码

    演示https://q3wph.csb.app/

    代码

    import * as React from "react";
    import { v4 as uuid } from "uuid";
    import Button from "./components/Button";
    import Input from "./components/Input";
    import Switch from "./components/Switch";
    import "./styles.css";
    
    export default function App() {
      const [list, setList] = React.useState([]);
      const [editRow, setEditRow] = React.useState({
        id: "",
        value: ""
      });
      const [newRowValue, setNewRowValue] = React.useState("");
      const [newRowIsEditable, setNewRowEditable] = React.useState(true);
      const [newRowIsRemovable, setNewRowRemovable] = React.useState(true);
    
      const removeItem = (removeId) => {
        setList((prevState) => prevState.filter(({ id }) => id !== removeId));
      };
    
      const editItem = (editId) => {
        const { name } = list.find((item) => item.id === editId);
        setEditRow({ id: editId, value: name });
      };
    
      const updateRowItem = ({ target: { value } }) => {
        setEditRow((prevState) => ({ ...prevState, value }));
      };
    
      const handleRowUpdate = (e) => {
        e.preventDefault();
    
        const { id, value } = editRow;
    
        if (!value) {
          alert("Please fill out the row input before updating the row!");
          return;
        }
    
        setList((prevState) =>
          prevState.map((item) =>
            item.id === id ? { ...item, name: value } : item
          )
        );
        setEditRow({ id: "", value: "" });
      };
    
      const addNewRowItem = (e) => {
        e.preventDefault();
    
        if (!newRowValue) {
          alert("Please fill out the new row item before submitting the form!");
          return;
        }
    
        setList((prevState) => [
          ...prevState,
          {
            id: uuid(),
            name: newRowValue,
            isEditable: newRowIsEditable,
            isRemovable: newRowIsRemovable
          }
        ]);
        setNewRowValue("");
        setNewRowEditable(true);
        setNewRowRemovable(true);
      };
    
      return (
        <div className="app">
          <h1>Dynamically Add/Edit/Remove Row</h1>
          {list.length > 0 ? (
            list.map(({ id, name, isEditable, isRemovable }) => (
              <div className="uk-card uk-card-default uk-card-body" key={id}>
                {editRow.id === id ? (
                  <form onSubmit={handleRowUpdate}>
                    <Input
                      placeholder="Add a new row..."
                      value={editRow.value}
                      handleChange={updateRowItem}
                    />
                    <Button color="secondary" type="submit">
                      Update Row
                    </Button>
                  </form>
                ) : (
                  <>
                    <h2 className="uk-card-title">{name}</h2>
                    {isEditable && (
                      <Button
                        className="uk-margin-small-bottom"
                        color="primary"
                        type="button"
                        handleClick={() => editItem(id)}
                      >
                        Edit
                      </Button>
                    )}
                    {isRemovable && (
                      <Button
                        color="danger"
                        type="button"
                        handleClick={() => removeItem(id)}
                      >
                        Remove
                      </Button>
                    )}
                  </>
                )}
              </div>
            ))
          ) : (
            <div>(Empty List)</div>
          )}
          <form
            className="uk-card uk-card-default uk-card-body"
            onSubmit={addNewRowItem}
          >
            <Input
              placeholder="Add a new row..."
              value={newRowValue}
              handleChange={(e) => setNewRowValue(e.target.value)}
            />
            <Switch
              label="Editable"
              handleChange={(e) => setNewRowEditable(Boolean(e.target.checked))}
              name="Editable"
              value={newRowIsEditable}
            />
            <Switch
              label="Removable"
              handleChange={(e) => setNewRowRemovable(Boolean(e.target.checked))}
              name="Removable"
              value={newRowIsRemovable}
            />
            <Button
              className="uk-margin-small-bottom"
              color="secondary"
              type="submit"
            >
              Add Row
            </Button>
            <Button color="danger" type="button" handleClick={() => setList([])}>
              Reset List
            </Button>
          </form>
        </div>
      );
    }
    

    【讨论】:

      【解决方案3】:

      我将一个属性附加到 span,名为 index 并在 removeThisRow 中访问它。

      import "./styles.css";
      
      import React, { useState } from "react";
      
      function App() {
        const [list, setList] = useState([]);
      
        function addRow() {
          let newRow = {
            name: "Test",
            action: (
              <span index={list.length} onClick={(e) => removeThisRow(e)}>
                REMOVE
              </span>
            )
          };
      
          setList([...list, newRow]);
        }
      
        function removeThisRow(e) {
          // need this to remove the specific item from my list array...
          const index = e.target.getAttribute("index"); // got the index as a string
          // Do whatever you want
        }
      
        return (
          <div>
            {list.map((item) => (
              <div>
                {item.name} | {item.action}
              </div>
            ))}
      
            <div onClick={(e) => addRow()}>ADD ROW</div>
          </div>
        );
      }
      
      export default App;
      
      

      【讨论】:

      • 行不通。您正在设置不存在的索引。 list.length 是最后一个索引 + 1
      • 请仔细分析。它完美无缺。这是一个代码框codesandbox.io/s/vigilant-haze-t7j42?file=/src/App.js
      • 我现在仔细分析了一下,还是不行,有几个问题;您正在为 DOM 设置索引,这不是一个好习惯,尤其是当您可以轻松地从 map 函数获取 index 时。另外,如果我添加行(获取索引 0)添加另一个(索引 1)和另一个(索引 2)会发生什么;删除索引 1。我最终得到第 0 行和第 2 行(长度 2)添加行(索引 2),现在我有两个具有相同索引的条目,如果我删除一个,两者都会消失。
      猜你喜欢
      • 2021-02-04
      • 2018-01-24
      • 1970-01-01
      • 1970-01-01
      • 2022-12-03
      • 2021-03-23
      • 1970-01-01
      • 2020-07-30
      • 2020-12-15
      相关资源
      最近更新 更多