Cách dùng Key trong React để tránh xung đột thành phần

Làm thế nào bạn có thể thuyết phục React rằng hai phương thức dùng cùng một thành phần cần trạng thái riêng của chúng? Tất nhiên, đó là key trong React.

Key trong React

Cách tiếp cận React có thể khá phức tạp và bạn có thể gặp phải hành vi không mong muốn hoặc thậm chí là các lỗi tinh vi. Loại bỏ những lỗi như vậy thường khá khó nếu bạn chưa xác định được nguyên nhân.

Một lỗi cụ thể phát sinh khi bạn kết xuất có điều kiện cùng một thành phần với các thuộc tính khác nhau. Khám phá chi tiết lỗi này và tìm hiểu cách sử dụng các phím React để giải quyết nó nhé.

Các thành phần React không phải lúc nào cũng độc lập

Cú pháp đơn giản là một trong những lý do chính bạn nên học React. Tuy nhiên, dù có nhiều ưu điểm, framework này không phải là không có lỗi.

Lỗi mà bạn sẽ tìm hiểu ở đây xảy ra khi bạn kết xuất có điều kiện cùng một thành phần, nhưng chuyển cho nó các thuộc tính khác nhau.

Trong những trường hợp như thế này, React sẽ cho rằng hai thành phần giống nhau, vì vậy nó sẽ không bận tâm đến việc kết xuất thành phần thứ hai. Do đó, bất kỳ trạng thái nào bạn xác định trong thành phần đầu tiên sẽ tồn tại giữa các lần hiển thị.

Hãy xem xét ví dụ sau để thấy rõ điều đó. Đầu tiên, bạn có thành phần Counter:

import { useState, useEffect } from "react"

export function Counter({name}) {
  const [count, setCount] = useState(0)

  return(
    <div>
      <div>{name}</div>
      <button onClick={() => setCount(c => c - 1)}> - </button>
      <br />
      <button onClick={() => setCount(c => c + 1)}> + </button>
    </div>
  )
}

Thành phần Counter này chấp nhận một tên từ phần tử thông qua việc phá hủy đối tượng, đây là một cách để sử dụng các thuộc tính trong React. Sau đó, nó hiển thị tên trong <div>. Nó cũng trả về hai nút: một nút để giảm số đếm ở trạng thái và nút còn lại để tăng số lượng.

Hãy nhớ rằng không có gì sai với đoạn code trên. Lỗi xuất phát từ khối code sau (thành phần App), sử dụng bộ đếm:

import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return(
    <div>
      { isKingsley ? <Counter name="Kingsley" /> : <Counter name="Sally" /> }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

Theo mặc định, code trên hiển thị bộ đếm có tên Kingsley. Nếu bạn tăng bộ đếm lên năm và nhấp vào nút Swap, nó sẽ hiển thị bộ đếm thứ hai có tên là Sally.

Nhưng vấn đề là bộ đếm sẽ không đặt lại về trạng thái mặc định bằng 0 sau khi bạn hoán đổi chúng.

Lỗi này xảy ra do cả hai trạng thái hiển thị các phần tử giống nhau theo cùng một thứ tự. React không biết rằng bộ đếm "Kingsley" khác với bộ đếm "Sally". Sự khác biệt duy nhất là thuộc tính name, nhưng thật không may, React không sử dụng nó để phân biệt các phần tử.

Bạn có thể giải quyết vấn đề này theo hai cách. Đầu tiên làthay đổi DOM và làm cho hai cây khác nhau. Ví dụ: bạn có thể bọc bộ đếm đầu tiên bên trong phần tử <div> và bộ đếm thứ hai bên trong phần tử <section>:

import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return (
    <div>
      { isKingsley ? 
        (<div>
          <Counter name="Kingsley" /> 
        </div>)
        : 
        (<section>
          <Counter name="Sally" /> 
        </section>)
      }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

Nếu bạn tăng counter "Kingsley" và click Swap, trạng thái sẽ đặt lại về 0. Một lần nữa, điều này xảy ra do cấu trúc của hai cây DOM khác nhau.

Khi biến isKingsleytrue, cấu trúc sẽ là div > div > Counter (một div chứa một div chứa một Counter). Khi bạn hoán đổi trạng thái bộ đếm bằng nút bấm, cấu trúc trở thành div > section > Counter. Do sự khác biệt này, React sẽ tự động hiển thị Counter mới với trạng thái đặt lại.

Có thể không phải lúc nào bạn cũng muốn thay đổi cấu trúc đánh dấu như thế này. Trong trường hợp này, hãy thử cách thứ hai.

Dùng key để hiện một thành phần mới

Key cho phép React phân biệt giữa các phần tử trong quá trình kết xuất. Vì vậy, nếu bạn có hai phần tử hoàn toàn giống nhau và bạn muốn báo hiệu cho React rằng phần tử này khác với phần tử kia, bạn cần đặt thuộc tính key duy nhất cho mỗi phần tử.

Thêm một khóa cho mỗi bộ đếm như sau:

import { useState } from "react"
import { Counter } from "./Counter"

export default function App() {
  const [isKingsley, setIsKingsley] = useState(true)

  return(
    <div>
      { isKingsley ? 
        <Counter key="Kingsley" name="Kingsley" /> : 
        <Counter key="Sally" name="Sally" /> 
      }
      <br />
      <button onClick={() => setIsKingsley(k => !k)}> Swap </button>
    </div>
  )
}

Bây giờ, khi bạn tăng counter "Kingsley" và nhấp vào Swap, React sẽ hiển thị bộ đếm mới và đặt lại trạng thái về 0.

Bạn cũng nên sử dụng các key khi kết xuất một mảng mục cùng loại, vì React sẽ không biết sự khác biệt giữa mỗi mục.

export default function App() {
  const names = ["Kingsley", "John", "Ahmed"]

  return(
    <div>
      { names.map((name, index) => {
        return <Counter key={index} name={name} />
      })}
    </div>
  )
}

Khi gán phím, React sẽ kết hợp một bộ đếm riêng với từng mục. Bằng cách đó, nó có thể phản ánh thay đổi bất kỳ mà bạn đã thực hiện trên mảng.

Trên đây là cách dùng key của React để tránh xung đột thành phần. Hi vọng bài viết hữu ích với các bạn.

Chủ Nhật, 20/08/2023 08:33
31 👨 177
0 Bình luận
Sắp xếp theo