Mindset khi tạo ra library component

Mindset khi tạo ra library component

Trong suốt quá trình làm việc với Angular, chắc hẳn bạn đã từng nghe đến, hoặc phải làm library component. Trong bài viết này, chúng ta sẽ cùng nhau hiểu rõ hơn về mindset cần có khi tạo ra library component trong Angular nói riêng, và trong tất cả các công nghệ khác nói chung.

Đây là một series bài viết hướng dẫn từ mindset tới cách tiếp cận và ví dụ code minh họa khi làm library component trong Angular. Các phần đã có trong series:

Your Component, not ours

Với một component được thiết kế dành cho bạn, bạn không cần quá quan tâm tới việc sử dụng component đó như nào. Bạn cần cái nút có thêm icon ở đầu? Ok, hãy thêm input(<icon-name>); vào component và bạn có thể dễ dàng “tùy biến” nó theo nhu cầu của bạn.

Hặc nó cần hiển thị label nào đó dưới khung input? Ví dụ như 1 cái hint chẳng hạn? Ok, hãy thêm input(<hint>); vào và bạn có thể truyền bất cứ string nào vào và nó sẽ cực kì linh hoạt cho bạn.

Tuy nhiên, component đó chỉ linh hoạt cho bạn.

Với <app-button /> bạn vừa tạo, bạn có thể thêm icon, thêm hint theo ý muốn bạn. Nhưng nếu đó không còn là icon nữa mà là 1 cái string bình thường thôi? Hoặc nó là 1 HTML element checkbox chẳng hạn? Hay hint đó giờ cần thêm icon ở đầu, người dùng có thể hover vào hint đó để xem thêm thông tin chi tiết?

Hãy tưởng tượng lượng code mà bạn sẽ phải If-Else và số lần phải quay lại đống bùi nhùi đó để sửa, phục vụ theo nhu cầu của người sử dụng chúng.

Và đó chính là lý do tại sao chúng ta cần phải có một cái mindset khác khi tạo ra library component. Một mindset mà những thư viện UI hàng đầu bên người họ hàng ReactJS của cúng ta đang tích cực áp dụng: “Composable”.

Chuẩn bị mindset

Một component cần phải hoạt động đúng chức năng của nó, đó là điều đương nhiên. Nhưng với một component được thiết kế để được dùng ở bất cứ đâu, một library component, bạn cần phải có sự linh hoạt, dễ mở rộng, và quan trọng nhất: dễ đoán.

Tại phần này, chúng ta cần phải tiếp cận và ghi nhớ tới những tôn chỉ sau:

If you want, bring your own

Component của bạn phải được thiết kế để người sử dụng có thể tùy biến theo ý muốn, đó là điều đương nhiên. Để phù hợp với 2-3 ý kiến thì dễ, nhưng để phù hợp với hàng trăm ý kiến khác nhau thì bạn không thể if-else hay switch-case cho tất cả được. Đó là lý do tại sao bạn cần phải thiết kế sao cho người component của mình có thể tháo lắp các phần của component mà họ muốn, và thay vào đó là thứ họ muốn.

Tuy nhiên, component của bạn vẫn sẽ phải hoạt động được với những người dễ tính, không muốn tùy biến gì cả. Đó là lý do bạn vẫn phải cung cấp default options cho component của bạn.

Bằng cách tuân theo tôn chỉ này, bạn sẽ đáp ứng được tới 80-90% yêu cầu của người sử dụng chúng. Đôi khi, đó là những yêu cầu mà bạn cũng không nghĩ có thể xảy ra, vì giờ họ có thể tự thay thế bất cứ thứ gì mà họ muốn, bằng thứ của họ.

Với phần nhỏ còn lại mà component của bạn không thể đáp ứng được, hãy tiếp tục đọc để biết hướng giải quyết nhé 😁.

Easy to predict

Component của bạn cần phải dễ đoán. Bản chất của developer là một người lười biếng, họ không muốn phải đọc document để biết component của bạn hoạt động như thế nào. Họ chỉ quan tâm tới suggestion của IDE gợi ý ra cái tên prop mà họ muốn sử dụng, và họ muốn nó hoạt động theo cách mà họ nghĩ.

Lấy ví dụ (click) event của một cái nút, bạn không cần phải đọc document để biết rằng bạn cần phải sử dụng (click) để bắt sự kiện click của cái nút đó. Khi cái nút bị [disabled]="true", bạn expect rằng nó sẽ không thể emit event click được. Khi cái nút có [loading]="true", bạn expect rằng nó cũng sẽ không thể emit event click được.

Có một vài cách dễ dàng để đạt được điều này:

  • Đừng cố gắng thay đổi tên khác so với cái tên gốc. (ví dụ: isDisabled thay vì disabled)
  • Đừng cố gắng thay đổi hành vi mặc định của thứ bạn đang ghi đè.
  • Đừng cố gắng tạo ra lại một cái bánh xe khác nếu bạn có thể sử dụng cái bánh xe sẵn có (hãy sử dụng <button />, thay vì bọc nó trong 1 cái component và phải forward các props sang một cái <button/> bên trong chỉ để styling nó).
  • Hạn chế side-effect của event từ component của bạn.

Thứ có được từ mindset này là component của bạn sẽ ít bug hơn, dễ dàng để người khác sử dụng hơn vì họ dễ dàng đoán được hành vi và sẽ xài component của bạn dễ chịu hơn.

Comments are our best friends

Với một component được thiết kế để dùng ở bất cứ đâu, bạn cần phải có những comment đầy đủ, rõ ràng, và dễ hiểu. Developer sẽ thà đọc comment hiện ra khi họ hover property hint trong một thẻ input, thay vì phải bỏ thêm 5-10 phút mò vào document và tìm nơi giải thích về property hint đó, cho dù nó có rõ ràng và đầy đủ hơn.

Tất nhiên, bạn không thể comment mọi thứ, nhưng ít nhất, bạn cần phải comment những thứ mà bạn nghĩ sẽ khiến người sử dụng khó hiểu, hoặc có thể sẽ không lường trước được toàn bộ hành vi của nó.

Hãy comment cho:

  • Bản thân component đó: Mô tả về component, nó làm gì và được tạo ra để giải quyết vấn đề gì.
  • TẤT CẢ các properties ĐẶC THÙ: Những properties không tồn tại mặc định, hoặc có hành vi khác so với mặc định.
  • TẤT CẢ các event: Đúng vậy, tất cả các event, kể cả mặc định. Đôi khi developer có thể bị overthinking với cả những event mặc định như (click) của một cái nút nó có side-effect gì không.
  • TẤT CẢ các slot: Nếu component của bạn hỗ trợ slot, hãy comment cho nó. Developer sẽ cần biết sự tồn tại của những slot đó, và cách sử dụng chúng.

Comment không chỉ giúp cho người sử dụng hiểu rõ hơn về component của bạn và có thể sử dụng nó một cách hiệu quả nhất; nó còn giúp cho bạn dễ dàng pick up lại code của mình sau một thời gian không sử dụng.

Prepare your SDK!

Như đã đề cập tại mindset đầu tiên , sẽ có một số yêu cầu mà component của bạn không thể đáp ứng được với cách tùy biến tại những chỗ bạn đã chỉ định (ví dụ như hoàn toàn thay đổi layout của component, hoặc thay đổi loại dữ liệu của component một cách hoàn toàn nhưng vẫn giữ các hành vi vốn có - data type từ string -> date chẳng hạn).

Những lúc này, việc có một bộ SDK để người sử dụng một phần logic từ component có sẵn, và hoàn toàn làm chủ phần còn lại sẽ là điều cực kì tuyệt vời cho bất kì ai đang sử dụng library component của bạn. Một SDK có thể chỉ đơn giản là một service đã implement sẵn logic khi loading/readonly/disabled thì sẽ trả ra 1 trạng thái unavailable chẳng hạn, hoặc xử lý luồng logic mà không quan trọng tới data type.

Input Base - SDK cho component input

SDK cho component input, xử lý luồng logic invalid, readonly, disabled, và một loạt condition trong đó. Người dùng chỉ cần extendsoverride những phần mà họ muốn, và component sẽ có những behavior như component gốc, với những behavior mà họ đã override.

Show me the code!

Vậy là với chỗ mindsets trên, chúng ta sẽ bắt tay vào áp dụng chúng như nào trong code?

Với mỗi một loại công nghệ sẽ có cách ứng dụng khác nhau, và vì series này đang tập trung vào Angular, vậy nên tại phần 2 chúng ta sẽ cùng nhau tìm hiểu về cách tiếp cận khi làm library component trong Angular thôi nhé.

Who wrote this?

Author Do Hoang Nam

Do Hoang Nam

Technical Leader at SmartOSC