Truyền props vào 1 Component

Các component React sử dụng props để giao tiếp với nhau. Mỗi component cha có thể truyền một số thông tin cho các component con của nó bằng cách cung cấp cho chúng các props. Props có thể làm bạn nhớ đến các thuộc tính HTML, nhưng bạn có thể truyền bất kỳ giá trị JavaScript nào thông qua chúng, bao gồm cả đối tượng, mảng và hàm.

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

  • Cách truyền props cho một component
  • Cách đọc props từ một component
  • Cách chỉ định các giá trị mặc định cho props
  • Cách truyền một số JSX cho một component
  • Cách props thay đổi theo thời gian

Các props quen thuộc

Props là thông tin mà bạn truyền cho một thẻ JSX. Ví dụ: className, src, alt, widthheight là một số props bạn có thể truyền cho một <img>:

function Avatar() {
  return (
    <img
      className="avatar"
      src="https://i.imgur.com/1bX5QH6.jpg"
      alt="Lin Lanying"
      width={100}
      height={100}
    />
  );
}

export default function Profile() {
  return (
    <Avatar />
  );
}

Các props bạn có thể truyền cho một thẻ <img> được xác định trước (ReactDOM tuân theo tiêu chuẩn HTML). Nhưng bạn có thể truyền bất kỳ props nào cho các component của riêng bạn, chẳng hạn như <Avatar>, để tùy chỉnh chúng. Đây là cách thực hiện!

Truyền props cho một component

Trong đoạn code này, component Profile không truyền bất kỳ props nào cho component con của nó, Avatar:

export default function Profile() {
return (
<Avatar />
);
}

Bạn có thể cung cấp cho Avatar một số props trong hai bước.

Bước 1: Truyền props cho component con

Đầu tiên, hãy truyền một số props cho Avatar. Ví dụ: hãy truyền hai props: person (một đối tượng) và size (một số):

export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}

Note

Nếu dấu ngoặc nhọn kép sau person= làm bạn bối rối, hãy nhớ lại chúng chỉ là một đối tượng bên trong dấu ngoặc nhọn JSX.

Bây giờ bạn có thể đọc các props này bên trong component Avatar.

Bước 2: Đọc props bên trong component con

Bạn có thể đọc các props này bằng cách liệt kê tên của chúng person, size được phân tách bằng dấu phẩy bên trong ({}) ngay sau function Avatar. Điều này cho phép bạn sử dụng chúng bên trong code Avatar, giống như bạn làm với một biến.

function Avatar({ person, size }) {
// person và size có sẵn ở đây
}

Thêm một số logic vào Avatar sử dụng các props personsize để hiển thị, và bạn đã hoàn thành.

Bây giờ bạn có thể định cấu hình Avatar để hiển thị theo nhiều cách khác nhau với các props khác nhau. Hãy thử điều chỉnh các giá trị!

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

function Avatar({ person, size }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={size}
      height={size}
    />
  );
}

export default function Profile() {
  return (
    <div>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi', 
          imageId: 'YfeOqp2'
        }}
      />
      <Avatar
        size={80}
        person={{
          name: 'Aklilu Lemma', 
          imageId: 'OKS67lh'
        }}
      />
      <Avatar
        size={50}
        person={{ 
          name: 'Lin Lanying',
          imageId: '1bX5QH6'
        }}
      />
    </div>
  );
}

Props cho phép bạn suy nghĩ về các component cha và con một cách độc lập. Ví dụ: bạn có thể thay đổi các props person hoặc size bên trong Profile mà không cần phải suy nghĩ về cách Avatar sử dụng chúng. Tương tự, bạn có thể thay đổi cách Avatar sử dụng các props này mà không cần nhìn vào Profile.

Bạn có thể coi props như “các núm” mà bạn có thể điều chỉnh. Chúng đóng vai trò tương tự như các đối số cho các hàm—thực tế, props đối số duy nhất cho component của bạn! Các hàm component React chấp nhận một đối số duy nhất, một đối tượng props:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Thông thường, bạn không cần toàn bộ đối tượng props mà chỉ cần tách nó thành các props riêng lẻ.

Chú Ý

Đừng bỏ lỡ cặp dấu ngoặc nhọn {} bên trong () khi khai báo props:

function Avatar({ person, size }) {
// ...
}

Cú pháp này được gọi là “destructuring” và tương đương với việc đọc các thuộc tính từ một tham số hàm:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

Chỉ định một giá trị mặc định cho một prop

Nếu bạn muốn cung cấp cho một prop một giá trị mặc định để dự phòng khi không có giá trị nào được chỉ định, bạn có thể thực hiện việc đó bằng cách destructuring bằng cách đặt = và giá trị mặc định ngay sau tham số:

function Avatar({ person, size = 100 }) {
// ...
}

Bây giờ, nếu <Avatar person={...} /> được hiển thị mà không có prop size, thì size sẽ được đặt thành 100.

Giá trị mặc định chỉ được sử dụng nếu prop size bị thiếu hoặc nếu bạn truyền size={undefined}. Nhưng nếu bạn truyền size={null} hoặc size={0}, giá trị mặc định sẽ không được sử dụng.

Chuyển tiếp props với cú pháp spread JSX

Đôi khi, việc truyền props trở nên rất lặp đi lặp lại:

function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}

Không có gì sai với code lặp đi lặp lại—nó có thể dễ đọc hơn. Nhưng đôi khi bạn có thể coi trọng sự ngắn gọn. Một số component chuyển tiếp tất cả các props của chúng cho các component con của chúng, giống như cách Profile này làm với Avatar. Vì chúng không sử dụng bất kỳ props nào của chúng trực tiếp, nên có thể hợp lý khi sử dụng cú pháp “spread” ngắn gọn hơn:

function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}

Điều này chuyển tiếp tất cả các props của Profile cho Avatar mà không cần liệt kê từng tên của chúng.

Sử dụng cú pháp spread một cách hạn chế. Nếu bạn đang sử dụng nó trong mọi component khác, thì có điều gì đó không ổn. Thông thường, nó chỉ ra rằng bạn nên chia các component của mình và truyền các children dưới dạng JSX. Thêm về điều đó tiếp theo!

Truyền JSX dưới dạng children

Việc lồng các thẻ trình duyệt tích hợp sẵn là điều phổ biến:

<div>
<img />
</div>

Đôi khi bạn sẽ muốn lồng các component của riêng bạn theo cùng một cách:

<Card>
<Avatar />
</Card>

Khi bạn lồng nội dung bên trong một thẻ JSX, component cha sẽ nhận nội dung đó trong một prop có tên là children. Ví dụ: component Card bên dưới sẽ nhận một prop children được đặt thành <Avatar /> và hiển thị nó trong một div bao bọc:

import Avatar from './Avatar.js';

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

Hãy thử thay thế <Avatar> bên trong <Card> bằng một số văn bản để xem component Card có thể bao bọc bất kỳ nội dung lồng nhau nào. Nó không cần phải “biết” những gì đang được hiển thị bên trong nó. Bạn sẽ thấy mẫu linh hoạt này ở nhiều nơi.

Bạn có thể coi một component có prop children như có một “lỗ” có thể được “lấp đầy” bởi các component cha của nó bằng JSX tùy ý. Bạn sẽ thường sử dụng prop children cho các trình bao bọc trực quan: bảng điều khiển, lưới, v.v.

Một ô Card giống như một câu đố với một khe cắm cho các mảnh "children" như văn bản và Avatar

Illustrated by Rachel Lee Nabors

Cách props thay đổi theo thời gian

Component Clock bên dưới nhận hai props từ component cha của nó: colortime. (Code của component cha bị bỏ qua vì nó sử dụng state, mà chúng ta sẽ không đi sâu vào ngay bây giờ.)

Hãy thử thay đổi màu trong hộp chọn bên dưới:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}

Ví dụ này minh họa rằng một component có thể nhận các props khác nhau theo thời gian. Props không phải lúc nào cũng tĩnh! Ở đây, prop time thay đổi mỗi giây và prop color thay đổi khi bạn chọn một màu khác. Props phản ánh dữ liệu của một component tại bất kỳ thời điểm nào, thay vì chỉ trong lúc ban đầu.

Tuy nhiên, props là bất biến—một thuật ngữ từ khoa học máy tính có nghĩa là “không thể thay đổi”. Khi một component cần thay đổi các props của nó (ví dụ: để đáp ứng tương tác của người dùng hoặc dữ liệu mới), nó sẽ phải “yêu cầu” component cha của nó truyền cho nó các props khác nhau—một đối tượng mới! Các props cũ của nó sau đó sẽ bị loại bỏ và cuối cùng công cụ JavaScript sẽ thu hồi bộ nhớ mà chúng chiếm giữ.

Đừng cố gắng “thay đổi props”. Khi bạn cần phản hồi đầu vào của người dùng (như thay đổi màu đã chọn), bạn sẽ cần “đặt state”, bạn có thể tìm hiểu về điều đó trong State: Bộ nhớ của Component.

Tóm tắt

  • Để truyền props, hãy thêm chúng vào JSX, giống như bạn làm với các thuộc tính HTML.
  • Để đọc props, hãy sử dụng cú pháp destructuring function Avatar({ person, size }).
  • Bạn có thể chỉ định một giá trị mặc định như size = 100, được sử dụng cho các props bị thiếu và undefined.
  • Bạn có thể chuyển tiếp tất cả các props bằng cú pháp spread JSX <Avatar {...props} />, nhưng đừng lạm dụng nó!
  • JSX lồng nhau như <Card><Avatar /></Card> sẽ xuất hiện dưới dạng prop children của component Card.
  • Props là các ảnh chụp chỉ đọc tại một thời điểm: mỗi lần hiển thị nhận được một phiên bản props mới.
  • Bạn không thể thay đổi props. Khi bạn cần tương tác, bạn sẽ cần đặt state.

Challenge 1 of 3:
Trích xuất một component

Component Gallery này chứa một số đánh dấu rất giống nhau cho hai profile. Hãy trích xuất một component Profile ra khỏi nó để giảm sự trùng lặp. Bạn sẽ cần chọn những props nào để truyền cho nó.

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

export default function Gallery() {
  return (
    <div>
      <h1>Notable Scientists</h1>
      <section className="profile">
        <h2>Maria Skłodowska-Curie</h2>
        <img
          className="avatar"
          src={getImageUrl('szV5sdG')}
          alt="Maria Skłodowska-Curie"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Profession: </b> 
            physicist and chemist
          </li>
          <li>
            <b>Awards: 4 </b> 
            (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal)
          </li>
          <li>
            <b>Discovered: </b>
            polonium (chemical element)
          </li>
        </ul>
      </section>
      <section className="profile">
        <h2>Katsuko Saruhashi</h2>
        <img
          className="avatar"
          src={getImageUrl('YfeOqp2')}
          alt="Katsuko Saruhashi"
          width={70}
          height={70}
        />
        <ul>
          <li>
            <b>Profession: </b> 
            geochemist
          </li>
          <li>
            <b>Awards: 2 </b> 
            (Miyake Prize for geochemistry, Tanaka Prize)
          </li>
          <li>
            <b>Discovered: </b>
            a method for measuring carbon dioxide in seawater
          </li>
        </ul>
      </section>
    </div>
  );
}