5 Practices về cấu trúc khi làm việc với React

Mở đầu

Trong công nghệ hiện nay chắc chúng ta không cần phải nghi ngờ gì về việc React là một thư viện tuyệt vời, đã tạo ra môt cuộc cách mạng xây dựng các giao diện cho người dùng. Chúng thật dễ để học và tìm hiểu, tạo điều kiện tuyệt vời cho phép ta tạo ra những thành phần, bộ phận có khả năng tái sử dụng lại được ở nhiều nơi, hỗ trợ cho website bạn có một giao diện hoàn toàn đồng nhất.

Tuy nhiên, vì React chỉ quản lý các công việc ở tầng view của một phần mềm ứng dụng, cho nên React không bắt buộc ta phải tuân theo một quy trình cụ thể nào. Nhưng khó khăn từ react có thể sẽ khiến cho ta gặp trở ngại trong việc giữ cho code được sắp xếp, tổ chức một cách hợp lý khi Project của React ngày càng phát triển.

Tại công ty của tác giả bài viết 9elements với sản phẩm hàng đầu của họ là PhotoEditorSDK – một ứng dụng React quy mô lớn. Tác giả cùng với team đã chọn ra và cung cấp cho chúng ta một số practices tốt nhất mà họ đúc kết được trong việc tổ chức một ứng dụng React quy mô lớn.

1. Cấu trúc thư mục dự án

Thông thường các DEV sẽ viết code phù hợp cho việc tạo style và code cho các component được tách riêng biệt ra các thư mục khác nhau. Tất cả các style được viết vào một file CSS dùng chung được chia sẻ trong Project. Component được tách rời khỏi file chứa style của nó. Ở ví dụ dưới đây component FilterSlider được để ở thư mục component, style của nó được viết vào file photo-editor-sdk.scss và được đặt trong thư mục styles

Sau rất nhiều lần refactor, đã rút ra được kinh nghiệm  rằng phương pháp tiếp cận trên sẽ không tốt khi dự án ngày càng được phát triển. Trong tương lai, các component của ta sẽ cần phải được chia sẻ giữa các module trong dự án, hay giữa các dự án liên quan đến nhau. Vì vậy mà đội ngũ của nhóm đã chuyển sang sử dụng cách tổ chức thư mục lấy component làm trung tâm.

Ý tưởng ở đây là tất cả những đoạn code, style thuộc về một component (Ví dụ: JavaScript, CSS, assets, tests…) đều được đặt trong một trong folder. Điều này sẽ giúp chúng ta dễ dàng đóng gói code thành một npm module hoặc, trong trường hợp đang vội hay cần gấp, có thể chia sẻ folder này cho các dự án khác một cách đơn giản.

Import các component

Một nhược điểm của cách tổ chức thư mục trên đó là khi phải import một component nào đó, ta buộc phải import đầy đủ đường dẫn sao cho hợp lệ, như sau:

Nhưng có lẽ ai cũng muốn viết kiểu này hơn:

Một giải pháp đơn giản, ngây thơ cho vấn đề này đó là đổi tên file chính của component thành index.js:

Nhưng thật không may rằng, khi phải debug các React Component trên trình duyệt Chrome và khi các lỗi xảy ra, Debugger sẽ show ra cho bạn hàng loạt các file cùng tên index.js và đó là một lý do khiến ta không nên lựa chọn phương pháp.

Một giải pháp khác đó là sử dụng directory-named-webpack-plugin. Plugin này tạo ra một quy luật nhỏ cho webpack resolver, giúp webpack có thể tìm thấy một file JavaScript hoặc JSX có tên trùng với tên thư mục mà nó đang được import. Nhưng khuyết của phương pháp này đó là thư mục vendor sẽ bị gắn liền với webpack. Đó là một vấn đề với những người sử dụng Rollup để bundle các library. Việc update lên các phiên bản webpack gần đây luôn là điều khó khăn.

Giải pháp cuối cùng được lựa chọn có một chút mở rộng, nhưng nó sử dụng một cơ chế resolve tiêu chuẩn của Node.js, khiến mọi thứ trở nên chắc chắn hơn và tránh được các rủi ro tương lai. Tất cả những gì chúng ta cần làm là thêm một file package.json vào trong cấu trúc thư mục:

Và ngay bên trong file package.json, ta sử dụng thuộc tính main để set đầu vào cho component, như sau:

Với việc thêm như trên, chúng ta đã có thể import component như sau:

2. CSS trong Javascript

Tạo style và đặc biệt là tạo theme vẫn luôn là một trong những vấn đề khó mà ta phải đối mặt. Như đã để cập ở trên, thông thường ta hay sử dụng một file CSS (SCSS) đồ sộ chứa tất cả các class css. Và để tránh việc trùng lặp tên, ta thường sử dụng các prefix toàn cục và tuân theo BEM convention để đặt tên class.

Với công nghệ hiện đại và đầy đủ tính năng, phương pháp này có vẻ sẽ không phù hợp lắm và cần được thay thế. Sử dụng CSS modules là một trong những ý tưởng nhưng nó có thể gây ra một vài vấn đề về hiệu năng. Phương pháp tách CSS thông qua việc sử dụng Extract Text plugin của webpack cũng tạo ra một sự phụ thuộc nặng nề vào webpack khiến cho việc test sau này rất khó khăn.

Sau đó, một vài giải pháp CSS-in-JS rất là mới cũng đã được xem xét qua:

  • Styled Components: lựa chọn phổ biến nhất hiện nay với cộng đồng vô cùng lớn.
  • EmotionJS: một đối thủ cạnh tranh với Style components.
  • Glamorous: một giải pháp phổ biến khác.

Việc lựa chọn một trong các thư viện trên hoàn toàn phụ thuộc vào các trường hợp sử dụng của dự án:

  1. Bạn có cần một thư viện có thể xuất ra một file CSS đã được compile cho môi trường production không? EmotionJS có thể làm được điều này.
  2. Bạn có gặp phải vấn đề phức tạp trong việc tạo theme? Styled Components và Glamorous là bạn đồng hành phù hợp với bạn.
  3. Bạn cần chúng phải hoạt động tốt trên server? Đây không còn là vấn đề nữa với những phiên bản gần đây của các thư viện trên.

Hầu hêt với các project, Styled Components thường được lựa chọn nhiều nhất bởi tính năng mạnh mẽ và cộng đồng vô cùng lớn của nó. Thư viện này vô cùng hữu ích, đặc biệt là khi bạn gặp phải vấn đề tạo theme vô cùng phức tạp.

Cố gắng xây dựng các React Component chỉ đảm nhiệm một vai trò duy nhất

Khi xây dựng các component UI có tính trừu tượng cao, đôi khi rất là khó để có thể tách và lựa chọn các tiêu chí. Đến một thời điểm nào đó, component của bạn sẽ cần đến các logic nhất định từ model của bạn, và mọi thứ lúc này sẽ trở nên lộn xộn hơn. Ở các phần tiếp theo, một số phương pháp để DRY các component sẽ được giới thiệu. Các kĩ thuật dưới đây có thể sẽ trùng lặp về tính năng và việc lựa chọn kĩ thuật phù hợp cho kiến trúc của bạn thường phụ thuộc vào sở thích hơn thực tế. Nhưng hãy cùng lướt qua các trường hợp sử dụng trước:

  • Khi cần một cơ chế làm việc với các component có nội dung khác nhau đối với người dùng đăng nhập/chưa đăng nhập.
  • Khi cần render một table với nhiều phần tử <tbody> có thể collaspe.
  • Khi cần hiển thị các component khác nhau phụ thuộc vào state. Và các phần tiếp theo sẽ là giải pháp cho các vấn đề được miêu tả ở trên.

3. Higher-order Component (HOC)

Đôi khi bạn cần được đảm bảo rằng một React Component nào đó sẽ chỉ xuất hiện khi mà người dùng đã đăng nhập vào ứng dụng của bạn. Ban đầu, bạn sẽ thực hiện công việc kiểm tra này liên tục trong hàm render cho đến khi bạn nhận ra rằng mình đang lặp code rất là nhiều. Với nhiệm vụ phải DRY code của mình, sớm hay muộn bạn cũng sẽ nhận thấy rằng các higher-order component giúp cho bạn có thể tách và trừu tượng hóa một số tiêu chí của component.

Theo thuật ngữ của phát triển phần mềm, higher-order component giống như một dạng của decorator pattern. Một higher-order component (HOC) chỉ đơn thuần là một hàm nhận một React component làm tham số và trả về một React component khác. Hãy nhìn vào ví dụ sau:

Hàm requiresAuth nhận vào một component (WrappedComponent) là một tham số, component đó sẽ được “trang trí” với những tính năng mong muốn. Bên trong hàm, class AuthenticatedComponent sẽ render ra component đó và thêm vào các tính năng để kiểm tra xem người dùng có tồn tại không, nếu không sẽ redirect sang trang đăng nhập. Cuối cùng, component này sẽ được kết nối tới store của Redux và return về. Redux rất là có ích trong ví dụ này, nhưng không phải nhất thiết phải cần tới nó.

4. Truyền hàm render vào component với children prop

Việc tạo ra một Row của Table có thể collaspe không phải là một công việc dễ dàng. Bạn sẽ render nút collaspe như thế nào? Bạn sẽ hiển thị bảng con như thế nào khi table không collaspe? Với JSX 2.0 mọi thứ đã trở nên dễ dàng hơn một chút, giờ bạn đã có thể trở lại một mảng thay vì duy nhất một html tag. Dưới đây cũng là một ví dụ để bạn có thể hiểu một chút về pattern Function as children. Hãy xem table dưới đây:

Và một table body có thể collapse

Bạn chỉ cần đơn giản chuyền một function qua children, function được gọi trong hàm render của các component. Kĩ thuật này còn có thể được gọi là render callback hay trong các trường hợp đặc biệt render prop.

5. Render props

Thuật ngữ “render prop” được đặt ra bởi Michael Jackson, người đã gợi ý rằng pattern higher-order component lúc nào cũng có thể được thay thể bởi các component bình thường với một “render prop”. Ý tưởng ban đầu là truyền một React component vào trong một hàm có thể gọi dưới dạng một thuộc tính và gọi hàm này ở trong hàm render.

Hãy nhìn đoạn code sau:

Như bạn thấy, có một thuộc tính là render, là một function được gọi trong quá trình render. Hàm được gọi trong nó lấy về state hoàn chỉnh làm tham số của nó và trả về JSX. Và sau đây là công năng của nó:

Với đoạn code trên, tham số data và isLoading được destruct từ object state và được sử dụng để lấy về response của JSX. Trong trường hợp này, trong lúc promise chưa được fulfilled, thì một dòng “Loading” sẽ được hiển thị. Tùy thuộc vào bạn quyết định phần nào của state sẽ được truyền vào cho “render prop” và cách bạn sử dụng chúng cho giao diện của bạn. Vì render prop pattern là sự tổng quát của pattern Function as children, nên không có gì ngăn cản việc bạn có nhiều render props trong một component.

Nguồn từ Genk

Leave a Reply

Your email address will not be published. Required fields are marked *