从 div 数组中删除元素时的奇怪行为(React Hooks)

从 div 数组中删除元素时,我正在努力理解一种奇怪的行为。我想要做的是创建一个代表购买列表的 div 数组。每个购买都有一个删除按钮,该按钮必须只删除单击的那个。发生的情况是,当在购买 x 上单击删除按钮时,所有索引大于 x 的元素都将被删除。


任何帮助将不胜感激,包括语法建议:)


import React, { useState } from "react";



const InvestmentSimulator = () => {


  const [counter, increment] = useState(0);

  const [purchases, setPurchases] = useState([

    <div key={`purchase${counter}`}>Item 0</div>

  ]);


  function addNewPurchase() {

    increment(counter + 1);

    const uniqueId = `purchase${counter}`;


    const newPurchases = [

      ...purchases,

      <div key={uniqueId}>

        <button onClick={() => removePurchase(uniqueId)}>delete</button>

        Item number {uniqueId}

      </div>

    ];


    setPurchases(newPurchases);

  }


  const removePurchase = id => {

    setPurchases(

      purchases.filter(function(purchase) {

        return purchase.key !== `purchase${id}`;

      })

    );

  };


  const purchasesList = (

    <div>

      {purchases.map(purchase => {

        if (purchases.indexOf(purchase) === purchases.length - 1) {

          return (

            <div key={purchases.indexOf(purchase)}>

              {purchase}

              <button onClick={() => addNewPurchase()}>add</button>

            </div>

          );

        }

        return purchase;

      })}

    </div>

  );


  return <div>{purchasesList}</div>;

};

export default InvestmentSimulator;


月关宝盒
浏览 190回答 2
2回答

慕斯709654

您的代码存在几个问题,因此我将一次解决一个问题:不要将 JSX 存储在 state 中State 用于存储可序列化的数据,而不是 UI。您可以存储数字、布尔值、字符串、数组、对象等……但不要存储组件。保持 JSX 简单您返回的 JSX 有点复杂。您正在映射,但如果是最后一次购买purchases,则还会返回一个按钮。add添加按钮与映射购买无关,因此单独定义:return (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; // Map purchases&nbsp; &nbsp; &nbsp; &nbsp; {purchases.map(purchase => (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // The JSX for purchases is defined here, not in state&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div key={purchase.id}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => removePurchase(purchase.id)}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delete&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Item number {purchase.id}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; ))}&nbsp; &nbsp; &nbsp; &nbsp; // An add button at the end of the list of purchases&nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => addNewPurchase()}>add</button>&nbsp; &nbsp; </div>)由于我们不应该将 JSX 存储在 state 中,return 语句就是我们将 state 值转换为 JSX 的地方。不要给 setter 函数起混淆的名字。您已经创建了一个状态变量counter,并命名了 setter 函数increment。这是误导 - 该函数increment不会增加计数器,它会设置计数器。如果我调用increment(0),计数不会增加,它被设置为 0。与命名设置器函数保持一致。在 React 社区中公认的最佳实践是 setter 函数与它设置的变量具有相同的名称,并以单词 "set" 为前缀。换句话说,你的状态值是counter,所以你的 setter 函数应该被调用setCounter。这准确且描述了该函数的作用:const [counter, setCounter] = useState(0)状态是异步更新的——不要同步处理在addNewPurchase函数中,您有:increment(counter + 1)const uniqueId = `purchase${counter}`这不会按您期望的方式工作。例如:const [myVal, setMyVal] = useState(0)const updateMyVal = () => {&nbsp; console.log(myVal)&nbsp; setMyVal(1)&nbsp; &nbsp; &nbsp;&nbsp; console.log(myVal)}考虑上面的例子。第一个console.log(myVal)将登录0到控制台。您希望第二个console.log(myVal)记录什么?您可能会期望1,但它实际上0也会记录。在函数完成执行并且组件重新渲染之前,状态不会更新,因此 的值myVal永远不会在函数的中途改变。整个功能保持不变。在您的情况下,您正在创建一个具有旧值的ID counter。组件这是您的组件的更新版本:const InvestmentSimulator = () => {&nbsp; &nbsp; // Use sensible setter function naming&nbsp; &nbsp; const [counter, setCounter] = useState(0)&nbsp; &nbsp; // Don't store JSX in state&nbsp; &nbsp; const [purchases, setPurchases] = useState([])&nbsp; &nbsp; const addNewPurchase = () => {&nbsp; &nbsp; &nbsp; &nbsp; setCounter(prev => prev + 1)&nbsp; &nbsp; &nbsp; &nbsp; setPurchases(prev => [...prev, { id: `purchase${counter + 1}` }])&nbsp; &nbsp; }&nbsp; &nbsp; const removePurchase = id => {&nbsp; &nbsp; &nbsp; &nbsp; setPurchases(prev => prev.filter(p => p.id !== id))&nbsp; &nbsp; }&nbsp; &nbsp; // Keep your JSX simple&nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {purchases.map(purchase => (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div key={purchase.id}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => removePurchase(purchase.id)}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; delete&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </button>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Item number {purchase.id}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ))}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => addNewPurchase()}>add</button>&nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; )}最后的想法即使进行了这些更改,该组件仍需要进行一些重新设计。例如,使用计数器创建唯一 ID 不是一个好习惯。如果计数器被重置,项目将共享相同的 ID。我希望这些项目中的每一个最终都将存储更多的数据,而不仅仅是一个 ID,因此为每个项目提供一个与项目相关的唯一 ID,而不是与其在列表中的位置相关。

HUWWW

永远不要使用数组的索引作为键。查看这篇文章了解更多相关信息。如果你想使用 index 做一些我在下面做的事情。const purchasesList = (&nbsp; &nbsp; <div>&nbsp; &nbsp; &nbsp; {purchases.map((purchase, i) => {&nbsp; &nbsp; &nbsp; &nbsp; const idx = i;&nbsp; &nbsp; &nbsp; &nbsp; if (purchases.indexOf(purchase) === purchases.length - 1) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return (&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <div key={idx}>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {purchase}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <button onClick={() => addNewPurchase()}>add</button>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; );&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return purchase;&nbsp; &nbsp; &nbsp; })}&nbsp; &nbsp; </div>&nbsp; );
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript