[英]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>
);
您的代碼有幾個問題,所以我將一次通過一個:
狀態用於存儲可序列化數據,而不是 UI。 您可以存儲數字、布爾值、字符串、數組、對象等……但不要存儲組件。
您返回的 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 的地方。
您已經創建了一個狀態變量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.