Dependency Injection là một pattern phổ biến được implement trong rất nhiều framework và library. Nhìn sơ qua thì có vẻ React không có. Nhưng thật sự là React đã hỗ trợ built-in dependency injection là JSX, chắc hẳn là bạn đang dùng nó đó.
Dependency Injection in short
Dependency Injection giải quyết một bài toán phổ biến: “hardcoded dependencies” – nghĩa là các phần dependency được fix cứng trong code. Khi một object A phụ thuộc vào một object B và sau đó tạo ra một object thứ hai, thì lúc này dependency không thể thay đổi (vì đã bị hardcode).
Ví dụ, class Calculator
tạo một logger
service, được fix cứng là ConsoleLogger
, không thể thay đổi sau này:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">import</span> ConsoleLogger <span class="token keyword">from</span> <span class="token string">"./ConsoleLogger"</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Calculator</span> <span class="token punctuation">{</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>logger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConsoleLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">add</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>logger<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Adding </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>a<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> to </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>b<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Tuy nhiên khi thực hiện test class này, developers muốn “mock” thành phần logger service này (không log ra console nữa chẳng hạn). Hoặc khi chạy production, họ muốn xài SysLogger hoặc một logger khác ví dụ của Sentry (SaaS logger) thì đều không được vì dependency đã được hardcode.
Chúng ta có thể giải quyết vấn đề trên bằng một commit xài Dependency Injection như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token operator">-</span><span class="token keyword">import</span> ConsoleLogger <span class="token keyword">from</span> <span class="token string">'./ConsoleLogger'</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">Calculator</span> <span class="token punctuation">{</span> <span class="token operator">-</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">-</span> <span class="token keyword">this</span><span class="token punctuation">.</span>logger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConsoleLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token operator">-</span> <span class="token punctuation">}</span> <span class="token operator">+</span> <span class="token function">constructor</span><span class="token punctuation">(</span>logger<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>logger <span class="token operator">=</span> logger<span class="token punctuation">;</span> <span class="token operator">+</span> <span class="token punctuation">}</span> <span class="token function">add</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>logger<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Adding </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>a<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> to </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>b<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> a<span class="token operator">+</span>b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Giờ đây dependency không còn phụ thuộc vào lúc object A tạo object B nữa:
1 2 3 4 | <span class="token keyword">const</span> logger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConsoleLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> calculator <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Calculator</span><span class="token punctuation">(</span>logger<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> result <span class="token operator">=</span> calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// console hiển thị "Adding 1 to 2"</span> |
Và như vậy các developers có thể dễ dàng thay đổi dependency sau này:
1 2 3 4 | <span class="token keyword">const</span> logger <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NullLogger</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> calculator <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Calculator</span><span class="token punctuation">(</span>logger<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> result <span class="token operator">=</span> calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// console shows nothing</span> |
Nhờ vào Dependency Injection, code dường như được clean hơn, modular hoá rõ ràng hơn. Angular có một buil-in Dependency Injection Container, Symfony và Spring cũng thế. Nhưng có vẻ như React.js không có?
JSX hỗ trợ Dependency Injection
React hỗ trợ Dependency Injection mà không cần dependency injection container (như Angular), nhờ vào JSX.
Hãy nhìn vào component render product reviews trong table dưới đây:
Đây là đoạn code render table:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> <span class="token function-variable function">ReviewList</span> <span class="token operator">=</span> props <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>List</span> <span class="token attr-name">resource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>reviews<span class="token punctuation">"</span></span> <span class="token attr-name">perPage</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">50</span><span class="token punctuation">}</span></span> <span class="token spread"><span class="token punctuation">{</span><span class="token punctuation">...</span><span class="token attr-value">props</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Datagrid</span> <span class="token attr-name">rowClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>edit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>DateField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>date<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>CustomerField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customer_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ProductField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>product_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>RatingField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>rating<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TextField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Comment<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>StatusField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Datagrid</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>List</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Component <List> lấy dữ liệu từ route /review/
trong một REST API và truyền vào thêm một parameter là perPage
. Nhưng <List> không thực hiện viện render bảng reviews. Thay vào đó, nó để việc render cho các child components làm: <DataGrid>. Điều đó có nghĩa rằng <List> phụ thuộc vào <DataGrid> cho phần render, một paren-child relationship đã được tạo ra ở đây, chính là Dependency Injection.
Và như tất cả các dạng Dependency Injection nào khác, ta có thể đổi phần “dependency” rất dễ dàng. Chẳng hạn trong ví dụ trên, nếu ta muốn đổi thành phần render thành một “simple list” thay vì một “data grid”, ta chỉ cần thay thế child của <List> bằng một component khác:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">import</span> <span class="token punctuation">{</span> List<span class="token punctuation">,</span> Datagrid<span class="token punctuation">,</span> TextField<span class="token punctuation">,</span> DateField <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react-admin'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">PostList</span> <span class="token operator">=</span> props <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>List</span> <span class="token attr-name">resource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>posts<span class="token punctuation">"</span></span> <span class="token attr-name">perPage</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">50</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>SimpleList</span> <span class="token attr-name">primaryText</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>review <span class="token operator">=></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ItemTitle</span> <span class="token attr-name">record</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>review<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">}</span></span> <span class="token attr-name">secondaryText</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>review <span class="token operator">=></span> review<span class="token punctuation">.</span>body<span class="token punctuation">}</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>List</span><span class="token punctuation">></span></span><span class="token plain-text"> ) </span> |
React thậm chí còn cho phép inject nhiều hơn một dependency trong một element. Đầu tiên, bởi vì một element có thể có nhiều hơn một child. Trong ví dụ trước, <DataGrid> nhận vào một list của các columns cần được hiển thị:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> <span class="token function-variable function">ReviewList</span> <span class="token operator">=</span> props <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>List</span> <span class="token attr-name">resource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>reviews<span class="token punctuation">"</span></span> <span class="token attr-name">perPage</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">50</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Datagrid</span> <span class="token attr-name">rowClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>edit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>DateField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>date<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>CustomerField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customer_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ProductField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>product_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>RatingField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>rating<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TextField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Comment<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>StatusField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Datagrid</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>List</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Ngoài inject bằng các child components, ta còn có thể inject thông qua props, như bên dưới ta cũng có thể inject vào một DataGrid body khác:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">const</span> <span class="token function-variable function">ReviewList</span> <span class="token operator">=</span> props <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>List</span> <span class="token attr-name">resource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>reviews<span class="token punctuation">"</span></span> <span class="token attr-name">perPage</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">50</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text"> - </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Datagrid</span> <span class="token attr-name">rowClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>edit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> + </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Datagrid</span> <span class="token attr-name">rowClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>edit<span class="token punctuation">"</span></span> <span class="token attr-name">body</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>MyDatagridBody</span> <span class="token punctuation">/></span></span><span class="token punctuation">}</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>DateField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>date<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>CustomerField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customer_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ProductField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>product_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>RatingField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>rating<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TextField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Comment<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>StatusField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Datagrid</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>List</span><span class="token punctuation">></span></span><span class="token plain-text"> ) </span> |
Và vì mỗi dependency là một component, ta cũng có thể inject dependency vào dependency, như vầy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">const</span> <span class="token function-variable function">ReviewList</span> <span class="token operator">=</span> props <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>List</span> <span class="token attr-name">resource</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>reviews<span class="token punctuation">"</span></span> <span class="token attr-name">perPage</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token number">50</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token plain-text"> <Datagrid rowClick="edit" - body=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>MyDatagridBody</span> <span class="token punctuation">/></span></span><span class="token punctuation">}</span><span class="token plain-text"> + body=</span><span class="token punctuation">{</span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>MyDatagridBody</span> <span class="token attr-name">withBulkActions</span> <span class="token punctuation">/></span></span><span class="token punctuation">}</span><span class="token plain-text"> > </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>DateField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>date<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>CustomerField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customer_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ProductField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>product_id<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>RatingField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>rating<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TextField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>body<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Comment<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>StatusField</span> <span class="token attr-name">source</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>Datagrid</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>List</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span> |
Như vậy, JSX đã có những tính năng cơ bản của một Dependency Injection Container: cho phép di chuyển, thay thế linh động các dependency và cấu hình chúng.
React Context
Ở trên ta đã thấy việc dùng JSX để implement Dependency Injection trong JSX Template, vậy còn ở các service function thì sao? React cũng khuyến khích xài components cho các services luôn. Ví dụ, ta có thể truyền một translation service dùng props của component:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">const</span> <span class="token function-variable function">englishTranslator</span> <span class="token operator">=</span> message <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>message <span class="token operator">==</span> <span class="token string">"hello.world"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"Hello, World!"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token string">"Not yet translated"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Greeting</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> translate <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">{</span><span class="token function">translate</span><span class="token punctuation">(</span><span class="token string">"hello.world"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">.</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Greeting</span> <span class="token attr-name">translate</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>englishTranslator<span class="token punctuation">}</span></span> <span class="token punctuation">/></span></span><span class="token punctuation">;</span> |
Tuy nhiên các viết này có thể cồng kềnh nếu code nhiều và có các lớp nested. Vì vậy React cung cấp một thành phần khác để khai báo Dependency Injection là context
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useContext <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">englishMessages</span> <span class="token operator">=</span> message <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>message <span class="token operator">==</span> <span class="token string">"hello.world"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token string">"Hello, World!"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token string">"Not yet translated"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> TranslationContext <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">createContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">Greeting</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> translate <span class="token operator">=</span> <span class="token function">useContext</span><span class="token punctuation">(</span>TranslationContext<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">{</span><span class="token function">translate</span><span class="token punctuation">(</span><span class="token string">"hello.world"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text">.</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>TranslationContext.Provider</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>englishMessages<span class="token punctuation">}</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>Greeting</span> <span class="token punctuation">/></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>TranslationContext.Provider</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Tạm kết
Vì sao React không cung cấp một Dependency Injection Container như Angular? Đơn giản vì không cần thiết, JSX và context đã quá đủ để giúp các React app trở nên modular và testable hơn rồi.
Đây chính là một điểm mạnh của React: ngay cả với các ứng dụng lớn và large-scale, React vẫn làm tốt được mọi thứ mà không đỏi hỏi những framework lớn và cồng kềnh (như Angular ):