iT邦幫忙

2023 iThome 鐵人賽

DAY 8
0
Modern Web

30天React練功坊-攻克常見實務/面試問題系列 第 8

30天React練功坊-攻克常見實務/面試問題 Day8: Using index as key might be a huge disaster

  • 分享至 

  • xImage
  •  
tags: ItIron2023 react

前言

我們昨天用了一個很簡單的例子展示useEffect可能造成的memory leak issue,今天我們來看一個老範例,這是所有的react教學都特別強調的一點,同時你也會在console中不斷看到相關的warning,但你是否真的理解為什麼這樣寫會造成問題、又會造成什麼問題呢?
讓我們很快的來看一下吧!

本日題目

請觀察一下這個codesandbox

day8-demo-image

這是一個相當簡單的清單,在App組件內我們渲染了Fruit組件組成的清單,每個Fruit組件都有著一個開關以及一個刪除按鈕,一切看起來都很正常,直到你試著做了以下的操作。

  1. 展開Banana
  2. 點擊Banana旁的刪除按鈕

你會發現雖然Banana消失了,但Cherry組件卻展開了,請觀察以下的程式碼試著解釋並修復這個問題。

day8-demo-gif

day8-demo-image2

function App() {
  const [fruits, setFruits] = useState([
    { id: "a", name: "Apple", color: "Red" },
    { id: "b", name: "Banana", color: "Yellow" },
    { id: "c", name: "Cherry", color: "Red" }
  ]);

  const deleteFruit = (index) => {
    const newFruits = [...fruits];
    newFruits.splice(index, 1);
    setFruits(newFruits);
  };

  return (
    <div>
      <h1>Using key as index might be a huge disaster</h1>
      <ul>
        {fruits.map((fruit, index) => (
          <Fruit
            key={index}
            fruit={fruit}
            deleteFruit={() => deleteFruit(index)}
          />
        ))}
      </ul>
    </div>
  );
}

const Fruit = ({ fruit, deleteFruit }) => {
  const [isOpen, toggle] = useState(false);

  return (
    <li>
      <span onClick={() => toggle(!isOpen)}>{isOpen ? "▼" : "►"}</span>
      {fruit.name}
      <button onClick={deleteFruit}>Delete</button>
      {isOpen && <div>Color: {fruit.color}</div>}
    </li>
  );
};

export default App;

解答與基本解釋

雖然標題基本上已經劇透完畢了,相信你們也知道key是關鍵,但為什麼index作為key會造成這樣的問題呢? 這一樣又是個react的渲染機制,簡單來說在react渲染陣列時會依賴key作為辨識(identifier)元素的依據,同時react也會試圖去重複利用組件去提升渲染效率,因此當你今天刪除了key=1的組件,更新清單時react發現更新後key=1的組件其實還在(因為下方的Cherry遞補上來),為了達到最佳效率它便僅更新了其中一個props,也就是水果的名字,而其餘的state則因為重複使用而保存下來了。 這算是個常見但其實牽扯相當深的底層渲染邏輯,這邊僅僅是給一個相當粗略的說法,有興趣請自己去查完整的相關文獻!

既然了解原因了,那實際上你知道只要給他獨一無二的key值就可以幫助react辨別哪些組件真的遭到更新了,解法會相當相當的簡單

<ul>
  {fruits.map((fruit, index) => (
    <Fruit
      key={fruit.id}
      fruit={fruit}
      deleteFruit={() => deleteFruit(index)}
    />
  ))}
</ul>

總結

今天我們示範了為什麼用index作為key可能不是個好主意,在許多的教學中沒有給你明確的例子,只跟你說react會利用key作為判別組件狀態的幫手,我想今天這個例子應該能讓你稍微清楚一些,但我並不是說你永遠不能用index作為key,要是你今天的清單並不會有更新、刪除之類的操作,實際上用index作為key並不會有太大的問題,你要注意的僅是頻繁更新清單的情況!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
30天React練功坊-攻克常見實務/面試問題 Day7: Memeory leak with useEffect
下一篇
30天React練功坊-攻克常見實務/面試問題 Day9: Data fetch with useEffect not work as expected
系列文
30天React練功坊-攻克常見實務/面試問題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言