Hiển thị phần tử có điều kiện

Các phần tử JSX trên trang web của bạn thường sẽ hiển thị những nội dung khác nhau tùy thuộc vào một số điều kiện nhất định. Với React, bạn có thể hiển thị những phần tử đó một cách có điều kiện bằng những câu lệnh JavaScript như if, &&, và ? :.

Danh mục

  • Cách hiển thị nội dung khác nhau dựa trên một điều kiện nhất định
  • Cách thêm vào (hay loại bỏ) một phần tử dựa trên một điều kiện nhất định
  • Những cú pháp ngắn gọn thường gặp khi hiển thị phần tử có điều kiện với React

Trả về phần tử JSX có điều kiện

Lấy ví dụ về phần tử PackingList bên dưới. Nó có chứa những phần tử con Item, những phần tử con này có một thuộc tính đi kèm là isPacked:

function Item({ name, isPacked }) {
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Để ý kĩ ta sẽ thấy những phần tử con Item có điều kiện (thuộc tính) đi kèm isPacked khác nhau (một số là true trong khi một số khác lại là false). Giả sử ta muốn thêm vào một dấu tích xanh (✔) cho những phần tử con Item thỏa mãn điều kiện: isPacked={true}.

Ta có thể thực hiện điều đó bằng câu lệnh if/else như sau:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

Nếu thuộc tính isPackedtrue, đoạn code trên sẽ chỉ trả về những phần tử Item có thuộc tính isPacked thỏa mãn điều kiện đó. Như vậy ta sẽ có được kết quả như bên dưới:

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Nếu thay đổi giá trị thuộc tính isPacked của những phần tử Item ở đoạn code trên, ta sẽ thấy những dấu tích được thêm vào/loại bỏ tùy thuộc vào giá trị thuộc tính isPacked.

Để ý kĩ ta sẽ thấy cú pháp ifreturn bên trên rất giống với những đoạn code JavaScript thường gặp, đó là vì React sử dụng JavaScript để trả về những phần tử JSX một cách có điều kiện.

Hiển thị phần tử “rỗng” (null) có điều kiện

Trong một vài trường hợp nếu điều kiện hiển thị một phần tử không được thỏa mãn, ta hoàn toàn có thể không hiển thị phần tử đó. Để thực hiện điều đó, ta có thể trả về một giá trị rỗng (null) như đoạn code bên dưới:

if (isPacked) {
return null;
}
return <li className="item">{name}</li>;

Dựa vào đoạn code trên ta thấy những phần tử Item thỏa mãn điều kiện isPacked === true sẽ không được hiển thị (return null) và ngược lại.

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Trên thực tế, việc trả về một giá trị null không phải là một cách thức phổ biến để ẩn đi một phần tử. Thường thì ta nên loại bỏ hay thêm vào một phần tử JSX theo cách áp dụng sau đây:

Thêm vào (hay loại bỏ) một phần tử JSX dựa trên một điều kiện nhất định

Từ ví dụ “dấu tích xanh” trước đó, để ý kĩ ta sẽ thấy có một lượng code khá giống nhau giữa 2 đoạn code

<li className="item">{name}</li>

<li className="item">{name}</li>

Chúng đều trả về phần tử <li className="item">...</li> và chỉ khác nhau ở mỗi nội dung bên trong (đó là dấu tích):

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

Việc trùng lặp code như vậy không hẳn sẽ đem lại một hiệu ứng gì đó quá tai hại, nhưng nó khiến cho việc kiểm soát và debug code khó khăn hơn. Ví dụ nếu ta muốn sửa đổi thuộc tính className, ta sẽ phải sửa đổi nó ở 2 chỗ khác nhau. Điều này tốn nhiều công sức và thời gian hơn khi ta phải đảm bảo rằng giá trị của className ở 2 nơi phải trùng khớp! Trong trường hợp như vậy, để giúp cho đoạn code trên được ngắn gọn và súc tích hơn, ta có thể áp dụng những phương pháp dưới đây:

Sử dụng toán tử ba ngôi (? :)

JavaScript cung cấp cho ta một cách thức để rút gọn những câu lệnh if dài dòng bằng cách sử dụng toán tử ba ngôi.

Sử dụng toán tử ba ngôi, ta có thể viết lại đoạn code dưới đây:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

như sau:

return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);

Đoạn code rút gọn bên trên có thể được hiểu là: “Nếu thuộc tính isPacked là true, thì (?) dấu tích ’✔’ sẽ được thêm vào ngay sau biến name, và ngược lại (:) thì ta sẽ chỉ hiện thị mỗi biến name mà thôi”

Chuyên sâu

Liệu 2 ví dụ trên có thực sự giống nhau?

Nếu đã quen thuộc với lập trình hướng đối tượng (OOP), bạn có thể cho rằng 2 ví dụ trên là khác nhau về mặt bản chất, vì một trong 2 ví dụ trên trả về một “đối tượng” (instance) <li>. Tuy nhiên, các phần tử JSX không phải là đổi tượng của một “lớp” (class) cụ thể nào vì chúng là “virtual DOM” (hay đúng hơn, chúng không thuộc về cây thư mục “thực”) và trên hết chúng không nắm dữ một trạng thái nội bộ (internal state) nào. Cho nên trên thực tế 2 ví dụ này là hoàn toàn tương đồng. Đọc thêm về Preserving and Resetting State.

Giả sử nếu muốn gạch đi (strike out) những Item mà có dấu tích bằng cách sử dụng phần tử <del>, bạn có thể thay đổi đoạn code như dưới dây:

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✔'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Ví dụ trên khá phổ biến, tuy nhiên trên thực tế khi phải làm việc với bất kì dự án React lớn nào, cú pháp trên có thể khiến codebase của bạn trở nên rườm rà và khó kiểm soát hơn. Khi có quá nhiều điều kiện khác nhau được gói gọn trong một phần tử, bạn nên tách chúng ra thành những phần tử con để mọi thứ trở nên gọn gàng hơn. Với React, bạn hoàn toàn có thể làm việc đó bằng cách gán chúng vào những biến (variable) khác nhau.

Toán tử logic AND (&&)

Một trong những phương thức rút gọn các đoạn code điều kiện khác đó là việc sử dụng toán tử AND (&&) trong JavaScript. Như đã bàn ở trên, nếu điều kiện hiển thị một phần tử không được thỏa mãn, ta hoàn toàn có thể không hiển thị phần tử đó. Với toán tử &&, ta có thể in ra dấu tích (✔) chỉ khi isPacked bằng true:

return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);

Đoạn code trên có thể được hiểu là “nếu isPacked bằng true, thì (&&) ta hiển thị dấu tích ’✔’, còn không thì sẽ không hiển thị gì cả”.

Tham khảo đoạn code dưới đây để hiểu rõ thêm:

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Toán tử && trong JavaScript trả về giá trị ở vế bên phải (trong trường hợp này đó chính là dấu tích ’✔’) nếu điều kiện ở vế bên trái là true. Tuy nhiên nếu điều kiện đó là false, giá trị ở về phải được trả về sẽ là false. Dưới con mắt của React, false có hiệu ứng tương tự như null hay undefined, cho nên nó sẽ không in ra gì cả.

Lưu ý

Cẩn thận khi đặt các giá trị số (numeric values) trước toán tử &&.

Để kiểm tra điều kiện được cho, JavaScript sẽ tự động chuyển đổi vế bên trái của && thành kiểu dữ liệu boolean. Tuy nhiên, nếu vế trái là số 0, thì toàn bộ biểu thức sẽ nhận giá trị là số (0) thay vì false, và React sẽ hiển thị 0 là kết quả cuối cùng, thay vì không hiển thị gì cả.

Ví dụ sau đây là một lỗi căn bản dành cho trường hợp trên: messageCount && <p>New messages</p>. Chúng ta rất dễ nhầm lẫn rằng đoạn code trên sẽ không hiển thị phần tử <p>messageCount bằng 0, mà dưới con mắt của JavaScript, 0 chính là một giá trị “sai” (falsy value), Tuy nhiên trong trường hợp này React sẽ hiển thị đúng giá trị 0 đó ra màn hình, thay vì không hiển thị gì cả!

Để khắc phục, ta nên chuyển đổi biểu thức bên tay trái thành kiểu dữ liệu boolean như sau: messageCount > 0 && <p>New messages</p>.

Gán biểu thức JSX vào biến (variable) một cách có điều kiện

Khi các điều kiện trở nên phức tạp khiến cho việc viết code trở nên rườm rà, hãy thử áp dụng phương pháp gán biến kết hợp với câu lệnh if. Lưu ý là những biến được khai báo bằng let hoàn toàn có thể được ghi đè, cho nên ta có thể khai báo biến itemContent bằng một giá trị mặc định như sau:

let itemContent = name;

Sau đó, ta sử dụng câu lệnh if để ghi đè giá trị của itemContent nếu isPacked bằng true:

if (isPacked) {
itemContent = name + " ✔";
}

Cuối cùng, việc sử dụng dấu ngoặc nhọn” sẽ giúp cho việc “nhúng” (embed) biến ở trên vào trong phần tử JSX:

<li className="item">
{itemContent}
</li>

Phương pháp trên khá dài dòng nhưng đồng thời cũng cực kì linh hoạt. Để hiểu rõ thêm hãy tham khảo đoạn code dưới đây:

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = name + " ✔";
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Phương pháp gán biến này cũng có thể được áp dụng với những biểu thức JSX bất kì như sau:

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✔"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

Nếu còn xa lạ với JavaScript, bạn có thể sẽ thấy choáng ngợp với muôn vàn cách viết code khác nhau cho cùng một vấn đề. Tuy nhiên việc hiểu biết về những cách thức trên sẽ giúp bạn trau dồi thêm kiến thức về JavaScript chứ không phải chỉ mỗi React thôi đâu! Để bắt đầu hãy chọn một phương án phù hợp với mình, và tham khảo lại chủ đề này trong tương lai.

Tóm tắt

  • Với React, bạn sử dụng JavaScript để kiểm soát những nhánh logic khác nhau.
  • Bạn có thể trả về các biểu thức JSX một cách có điều kiện bằng câu lệnh if.
  • Các biểu thức JSX có thể được lưu dưới dạng biến (variable) một cách có điều kiện. Sau đó chúng có thể được dùng (nhúng) vào các biểu thức JSX khác bằng cách sử dụng cặp dấu ngoặc nhọn { }.
  • Trong JSX, {cond ? <A /> : <B />} có thể được hiểu là “Nếu điều kiện cond được thỏa mãn, ta sẽ hiển thị (trả về) phần tử <A />, nếu không thì ta sẽ hiển thị phần tử <B />.
  • Trong JSX, {cond && <A />} có nghĩa là “Nếu điều kiện cond được thỏa mãn, ta sẽ trả về phần tử <A />, nếu không thì ta sẽ không hiển thị gì cả”.
  • Những phương thức rút gọn bên trên khá phổ biến, tuy nhiên bạn hoàn toàn có thể đạt được kết quả tương tự bằng cách sử dụng lệnh if.

Thử thách 1 trên 3:
Hiển thị icon cho những items chưa hoàn thành bằng toán tử 3 ngôi ? :

Sử dụng toán tử điều kiện (cond ? a : b) để hiển thị dấu ❌ nếu isPacked khác true.

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}