Bạn thường muốn hiển thị nhiều component tương tự từ một tập hợp dữ liệu. Bạn có thể sử dụng các phương thức mảng JavaScript để thao tác với một mảng dữ liệu. Trên trang này, bạn sẽ sử dụng filter()map() với React để lọc và chuyển đổi mảng dữ liệu của bạn thành một mảng các component.

Bạn sẽ được học

  • Cách hiển thị các component từ một mảng bằng cách sử dụng map() của JavaScript
  • Cách chỉ hiển thị các component cụ thể bằng cách sử dụng filter() của JavaScript
  • Khi nào và tại sao nên sử dụng các key của React

Hiển thị dữ liệu từ mảng

Giả sử bạn có một danh sách nội dung.

<ul>
<li>Creola Katherine Johnson: nhà toán học</li>
<li>Mario José Molina-Pasquel Henríquez: nhà hóa học</li>
<li>Mohammad Abdus Salam: nhà vật lý</li>
<li>Percy Lavon Julian: nhà hóa học</li>
<li>Subrahmanyan Chandrasekhar: nhà vật lý thiên văn</li>
</ul>

Sự khác biệt duy nhất giữa các mục danh sách đó là nội dung của chúng, dữ liệu của chúng. Bạn thường cần hiển thị một số phiên bản của cùng một component bằng cách sử dụng dữ liệu khác nhau khi xây dựng giao diện: từ danh sách các bình luận đến thư viện ảnh hồ sơ. Trong những tình huống này, bạn có thể lưu trữ dữ liệu đó trong các đối tượng và mảng JavaScript và sử dụng các phương thức như map()filter() để hiển thị danh sách các component từ chúng.

Dưới đây là một ví dụ ngắn gọn về cách tạo danh sách các mục từ một mảng:

  1. Di chuyển dữ liệu vào một mảng:
const people = [
'Creola Katherine Johnson: nhà toán học',
'Mario José Molina-Pasquel Henríquez: nhà hóa học',
'Mohammad Abdus Salam: nhà vật lý',
'Percy Lavon Julian: nhà hóa học',
'Subrahmanyan Chandrasekhar: nhà vật lý thiên văn'
];
  1. Ánh xạ các thành viên people vào một mảng mới các nút JSX, listItems:
const listItems = people.map(person => <li>{person}</li>);
  1. Trả về listItems từ component của bạn được bao bọc trong một thẻ <ul>:
return <ul>{listItems}</ul>;

Đây là kết quả:

const people = [
  'Creola Katherine Johnson: nhà toán học',
  'Mario José Molina-Pasquel Henríquez: nhà hóa học',
  'Mohammad Abdus Salam: nhà vật lý',
  'Percy Lavon Julian: nhà hóa học',
  'Subrahmanyan Chandrasekhar: nhà vật lý thiên văn'
];

export default function List() {
  const listItems = people.map(person =>
    <li>{person}</li>
  );
  return <ul>{listItems}</ul>;
}

Lưu ý sandbox ở trên hiển thị một lỗi trong bảng điều khiển:

Console
Warning: Mỗi phần tử con trong một danh sách nên có một prop “key” duy nhất.

Bạn sẽ học cách sửa lỗi này sau trên trang này. Trước khi chúng ta đi đến đó, hãy thêm một số cấu trúc vào dữ liệu của bạn.

Lọc mảng các mục

Dữ liệu này có thể được cấu trúc hơn nữa.

const people = [{
id: 0,
name: 'Creola Katherine Johnson',
profession: 'mathematician',
}, {
id: 1,
name: 'Mario José Molina-Pasquel Henríquez',
profession: 'chemist',
}, {
id: 2,
name: 'Mohammad Abdus Salam',
profession: 'physicist',
}, {
id: 3,
name: 'Percy Lavon Julian',
profession: 'chemist',
}, {
id: 4,
name: 'Subrahmanyan Chandrasekhar',
profession: 'astrophysicist',
}];

Giả sử bạn muốn một cách để chỉ hiển thị những người có nghề nghiệp là 'chemist'. Bạn có thể sử dụng phương thức filter() của JavaScript để chỉ trả về những người đó. Phương thức này lấy một mảng các mục, chuyển chúng qua một “bài kiểm tra” (một hàm trả về true hoặc false) và trả về một mảng mới chỉ gồm những mục đã vượt qua bài kiểm tra (trả về true).

Bạn chỉ muốn các mục mà profession'chemist'. Hàm “kiểm tra” cho việc này trông giống như (person) => person.profession === 'chemist'. Đây là cách để kết hợp nó:

  1. Tạo một mảng mới chỉ gồm những người là “chemist”, chemists, bằng cách gọi filter() trên people lọc theo person.profession === 'chemist':
const chemists = people.filter(person =>
person.profession === 'chemist'
);
  1. Bây giờ ánh xạ trên chemists:
const listItems = chemists.map(person =>
<li>
<img
src={getImageUrl(person)}
alt={person.name}
/>
<p>
<b>{person.name}:</b>
{' ' + person.profession + ' '}
known for {person.accomplishment}
</p>
</li>
);
  1. Cuối cùng, trả về listItems từ component của bạn:
return <ul>{listItems}</ul>;
import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const chemists = people.filter(person =>
    person.profession === 'chemist'
  );
  const listItems = chemists.map(person =>
    <li>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return <ul>{listItems}</ul>;
}

Chú Ý

Các hàm mũi tên trả về biểu thức ngay sau => một cách ngầm định, vì vậy bạn không cần câu lệnh return:

const listItems = chemists.map(person =>
<li>...</li> // Trả về ngầm định!
);

Tuy nhiên, bạn phải viết return một cách rõ ràng nếu => của bạn theo sau bởi dấu ngoặc nhọn {!

const listItems = chemists.map(person => { // Dấu ngoặc nhọn
return <li>...</li>;
});

Các hàm mũi tên chứa => { được cho là có “thân khối”. Chúng cho phép bạn viết nhiều hơn một dòng mã, nhưng bạn phải tự viết câu lệnh return. Nếu bạn quên nó, sẽ không có gì được trả về!

Giữ các mục danh sách theo thứ tự với key

Lưu ý rằng tất cả các sandbox ở trên đều hiển thị một lỗi trong bảng điều khiển:

Console
Warning: Mỗi phần tử con trong một danh sách nên có một prop “key” duy nhất.

Bạn cần cung cấp cho mỗi mục trong mảng một key — một chuỗi hoặc một số nhận dạng duy nhất nó giữa các mục khác trong mảng đó:

<li key={person.id}>...</li>

Note

Các phần tử JSX trực tiếp bên trong một lệnh gọi map() luôn cần các key!

Các key cho React biết mục mảng nào mà mỗi component tương ứng, để nó có thể khớp chúng sau này. Điều này trở nên quan trọng nếu các mục trong mảng của bạn có thể di chuyển (ví dụ: do sắp xếp), được chèn hoặc bị xóa. Một key được chọn tốt sẽ giúp React suy ra chính xác những gì đã xảy ra và thực hiện các cập nhật chính xác cho cây DOM.

Thay vì tạo các key một cách nhanh chóng, bạn nên đưa chúng vào dữ liệu của mình:

export const people = [{
  id: 0, // Được sử dụng trong JSX làm key
  name: 'Creola Katherine Johnson',
  profession: 'mathematician',
  accomplishment: 'spaceflight calculations',
  imageId: 'MK3eW3A'
}, {
  id: 1, // Được sử dụng trong JSX làm key
  name: 'Mario José Molina-Pasquel Henríquez',
  profession: 'chemist',
  accomplishment: 'discovery of Arctic ozone hole',
  imageId: 'mynHUSa'
}, {
  id: 2, // Được sử dụng trong JSX làm key
  name: 'Mohammad Abdus Salam',
  profession: 'physicist',
  accomplishment: 'electromagnetism theory',
  imageId: 'bE7W1ji'
}, {
  id: 3, // Được sử dụng trong JSX làm key
  name: 'Percy Lavon Julian',
  profession: 'chemist',
  accomplishment: 'pioneering cortisone drugs, steroids and birth control pills',
  imageId: 'IOjWm71'
}, {
  id: 4, // Được sử dụng trong JSX làm key
  name: 'Subrahmanyan Chandrasekhar',
  profession: 'astrophysicist',
  accomplishment: 'white dwarf star mass calculations',
  imageId: 'lrWQx8l'
}];

Tìm hiểu sâu

Hiển thị một số nút DOM cho mỗi mục danh sách

Bạn làm gì khi mỗi mục cần hiển thị không phải một, mà là một số nút DOM?

Cú pháp <>...</> Fragment ngắn gọn sẽ không cho phép bạn truyền một key, vì vậy bạn cần nhóm chúng thành một <div> duy nhất hoặc sử dụng cú pháp <Fragment> dài hơn và rõ ràng hơn:

import { Fragment } from 'react';

// ...

const listItems = people.map(person =>
<Fragment key={person.id}>
<h1>{person.name}</h1>
<p>{person.bio}</p>
</Fragment>
);

Các Fragment biến mất khỏi DOM, vì vậy điều này sẽ tạo ra một danh sách phẳng gồm <h1>, <p>, <h1>, <p>, v.v.

Nơi để lấy key của bạn

Các nguồn dữ liệu khác nhau cung cấp các nguồn key khác nhau:

  • Dữ liệu từ cơ sở dữ liệu: Nếu dữ liệu của bạn đến từ cơ sở dữ liệu, bạn có thể sử dụng các key/ID của cơ sở dữ liệu, vốn dĩ là duy nhất.
  • Dữ liệu được tạo cục bộ: Nếu dữ liệu của bạn được tạo và lưu trữ cục bộ (ví dụ: ghi chú trong một ứng dụng ghi chú), hãy sử dụng một bộ đếm tăng dần, crypto.randomUUID() hoặc một gói như uuid khi tạo các mục.

Các quy tắc của key

  • Các key phải là duy nhất giữa các anh chị em. Tuy nhiên, bạn có thể sử dụng cùng một key cho các nút JSX trong các mảng khác nhau.
  • Các key không được thay đổi nếu không điều đó sẽ phá vỡ mục đích của chúng! Không tạo chúng trong khi hiển thị.

Tại sao React cần các key?

Hãy tưởng tượng rằng các tệp trên màn hình của bạn không có tên. Thay vào đó, bạn sẽ tham khảo chúng theo thứ tự của chúng — tệp đầu tiên, tệp thứ hai, v.v. Bạn có thể làm quen với nó, nhưng một khi bạn xóa một tệp, nó sẽ trở nên khó hiểu. Tệp thứ hai sẽ trở thành tệp đầu tiên, tệp thứ ba sẽ là tệp thứ hai, v.v.

Tên tệp trong một thư mục và các key JSX trong một mảng phục vụ một mục đích tương tự. Chúng cho phép chúng ta xác định duy nhất một mục giữa các anh chị em của nó. Một key được chọn tốt cung cấp nhiều thông tin hơn vị trí trong mảng. Ngay cả khi vị trí thay đổi do sắp xếp lại, key cho phép React xác định mục trong suốt vòng đời của nó.

Chú Ý

Bạn có thể bị cám dỗ sử dụng chỉ mục của một mục trong mảng làm key của nó. Trên thực tế, đó là những gì React sẽ sử dụng nếu bạn không chỉ định key nào cả. Nhưng thứ tự mà bạn hiển thị các mục sẽ thay đổi theo thời gian nếu một mục được chèn, xóa hoặc nếu mảng được sắp xếp lại. Chỉ mục làm key thường dẫn đến các lỗi tinh vi và khó hiểu.

Tương tự, không tạo các key một cách nhanh chóng, ví dụ: với key={Math.random()}. Điều này sẽ khiến các key không bao giờ khớp giữa các lần hiển thị, dẫn đến tất cả các component và DOM của bạn được tạo lại mỗi lần. Điều này không chỉ chậm mà còn làm mất mọi dữ liệu đầu vào của người dùng bên trong các mục danh sách. Thay vào đó, hãy sử dụng một ID ổn định dựa trên dữ liệu.

Lưu ý rằng các component của bạn sẽ không nhận được key làm một prop. Nó chỉ được sử dụng như một gợi ý bởi chính React. Nếu component của bạn cần một ID, bạn phải chuyển nó như một prop riêng biệt: <Profile key={id} userId={id} />.

Tóm tắt

Trên trang này, bạn đã học:

  • Cách di chuyển dữ liệu ra khỏi các component và vào các cấu trúc dữ liệu như mảng và đối tượng.
  • Cách tạo các tập hợp các component tương tự với map() của JavaScript.
  • Cách tạo các mảng các mục được lọc với filter() của JavaScript.
  • Tại sao và cách đặt key trên mỗi component trong một tập hợp để React có thể theo dõi từng component ngay cả khi vị trí hoặc dữ liệu của chúng thay đổi.

Challenge 1 of 4:
Chia một danh sách thành hai

Ví dụ này hiển thị một danh sách tất cả mọi người.

Thay đổi nó để hiển thị hai danh sách riêng biệt liên tiếp: Các nhà hóa họcMọi người khác. Giống như trước đây, bạn có thể xác định xem một người có phải là nhà hóa học hay không bằng cách kiểm tra xem person.profession === 'chemist'.

import { people } from './data.js';
import { getImageUrl } from './utils.js';

export default function List() {
  const listItems = people.map(person =>
    <li key={person.id}>
      <img
        src={getImageUrl(person)}
        alt={person.name}
      />
      <p>
        <b>{person.name}:</b>
        {' ' + person.profession + ' '}
        known for {person.accomplishment}
      </p>
    </li>
  );
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}