簡體   English   中英

從 div 數組中刪除元素時的奇怪行為(React Hooks)

[英]Strange behaviour when removing element from array of divs (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;

永遠不要使用數組的索引作為鍵。 查看這篇文章,了解更多相關信息。 如果你想使用索引做一些我在下面做的事情。

const purchasesList = (
    <div>
      {purchases.map((purchase, i) => {
        const idx = i;
        if (purchases.indexOf(purchase) === purchases.length - 1) {
          return (
            <div key={idx}>
              {purchase}
              <button onClick={() => addNewPurchase()}>add</button>
            </div>
          );
        }
        return purchase;
      })}
    </div>
  );

您的代碼有幾個問題,所以我將一次通過一個:


不要將 JSX 存儲在 state 中

狀態用於存儲可序列化數據,而不是 UI。 您可以存儲數字、布爾值、字符串、數組、對象等……但不要存儲組件。


保持你的 JSX 簡單

您返回的 JSX 有點復雜。 您正在通過purchases進行映射,但如果是最后一次購買,則還返回一個add按鈕。 添加按鈕與映射購買無關,因此單獨定義它:

return (
    <div>
        // Map purchases
        {purchases.map(purchase => (
            // The JSX for purchases is defined here, not in state
            <div key={purchase.id}>
                <button onClick={() => removePurchase(purchase.id)}>
                    delete
                </button>
                Item number {purchase.id}
            </div>
        ))}
        // An add button at the end of the list of purchases
        <button onClick={() => addNewPurchase()}>add</button>
    </div>
)

由於我們不應該在狀態中存儲 JSX,因此 return 語句是我們將狀態值轉換為 JSX 的地方。


不要給 setter 函數起混淆的名字。

您已經創建了一個狀態變量counter ,並將 setter 函數命名為increment 這是一種誤導 - 函數increment不會增加計數器,而是設置計數器 如果我調用increment(0) ,則計數不會增加,而是設置為 0。

與命名 setter 函數保持一致。 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 = () => {
  console.log(myVal)
  setMyVal(1)     
  console.log(myVal)
}

考慮上面的例子。 第一個console.log(myVal)會將0記錄到控制台。 你期望第二個console.log(myVal)記錄什么? 您可能期望1 ,但它實際上也記錄0

在函數完成執行和組件重新渲染之前,狀態不會更新,因此myVal的值永遠不會在函數中途改變。 整個函數保持不變。

在您的情況下,您正在使用counter值創建一個 ID。


組件

這是您的組件的更新版本:

const InvestmentSimulator = () => {
    // Use sensible setter function naming
    const [counter, setCounter] = useState(0)

    // Don't store JSX in state
    const [purchases, setPurchases] = useState([])

    const addNewPurchase = () => {
        setCounter(prev => prev + 1)
        setPurchases(prev => [...prev, { id: `purchase${counter + 1}` }])
    }

    const removePurchase = id => {
        setPurchases(prev => prev.filter(p => p.id !== id))
    }

    // Keep your JSX simple
    return (
        <div>
            {purchases.map(purchase => (
                <div key={purchase.id}>
                    <button onClick={() => removePurchase(purchase.id)}>
                        delete
                    </button>
                    Item number {purchase.id}
                </div>
            ))}
            <button onClick={() => addNewPurchase()}>add</button>
        </div>
    )
}

最后的想法

即使進行了這些更改,該組件仍然需要進行一些重新設計。

例如,使用計數器創建唯一 ID 並不是一個好習慣。 如果計數器被重置,項目將共享相同的 ID。 我希望這些項目中的每一個最終都會存儲更多的數據,而不僅僅是一個 ID,所以給它們每個都一個唯一的 ID,該 ID 與項目相關,而不是與其在列表中的位置相關。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM