Lời mở đầu
Props là 1 phần quan trọng không thể thiếu trong mỗi ứng dụng React. Nó được dùng để truyền data từ Component Cha xuống các Component con.
Tuy nhiên nó chỉ là 1 cách trong rất nhiều cách mà React sử dụng để truyền Data.
Props là tham số truyền vào bên trong 1 Component
đối với Typescript, có 2 việc cần làm với 1 Props:
- export interface mà Component nhận được
- sử dụng interface đó trong Component
1 2 3 4 5 6 7 | <span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">WelcomeProps</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> string<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">const</span> Welcom <span class="token operator">=</span> <span class="token punctuation">(</span>props<span class="token operator">:</span> WelcomeProps<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token operator"><</span>h1<span class="token operator">></span>Hello<span class="token punctuation">,</span> <span class="token punctuation">{</span>props<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
một quy tắc nghiêm ngặt duy nhất:
Tất cả các React props phải hoạt động giống như các hàm thuần túy:
hàm thuần túy là những hàm không làm thay đổi tham số đầu vào: function sum(a, b) { return a + b; }
các hàm này sẽ không làm thay đổi kết quả với những đầu vào giống nhau
hàm không thuần túy thường gặp với những hàm có props là Object, vì Object có thể thay đổi giá trị các tham số trong nó sau khi ra khỏi hàm:
1 2 3 4 | <span class="token keyword">function</span> <span class="token function">withdraw</span><span class="token punctuation">(</span><span class="token parameter">account<span class="token punctuation">,</span> amount</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> account<span class="token punctuation">.</span>total <span class="token operator">-=</span> amount<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
vì 1 Project có thể thay đổi theo thời gian, nên chỉ props không đủ đáp ứng,
React tạo ra State để giải quyết vấn đề thay đổi tham số trong từng Component
Truyền props trong các components
Nhớ rằng các component có thể chấp nhận các props không giới hạn, kể cả các giá trị nguyên thủy, các React Component hoặc các function.
Bạn có thể truyền dữ liệu từ một Component tới 1 Component khác bằng cách truyền như một attributes trong HTML element như sau:
1 2 3 4 | <span class="token operator"><</span>Welcome tenProps1<span class="token operator">=</span><span class="token string">"giatri"</span> tenProps2 <span class="token operator">=</span> <span class="token string">"giatri2"</span><span class="token operator">></span> Giá trị của props<span class="token punctuation">.</span>children <span class="token operator"><</span><span class="token operator">/</span>Welcome<span class="token operator">></span> |
Vậy trong components Welcome giá trị của props sẽ là một object bao gồm các giá trị truyền vào :
1 2 | <span class="token punctuation">{</span> tenProps1<span class="token operator">:</span> <span class="token string">"giatri"</span><span class="token punctuation">,</span> tenProps2<span class="token operator">:</span> <span class="token string">"giatri2"</span><span class="token punctuation">,</span> children<span class="token operator">:</span> <span class="token string">"Giá trị của props.children"</span> <span class="token punctuation">}</span> |
Khi bạn truyền một giá trị bên trong một tags thì nó sẽ là giá trị của thuộc tính chirlden trong object props như bên trên
Nhận props trong components
Nhận props trong functional components bằng cách chỉ định tham số trong function.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">import</span> React <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">Welcome</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span> <span class="token comment">//Giá trị của props</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div<span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>Xin chào <span class="token punctuation">{</span>props<span class="token punctuation">.</span>tenProps1<span class="token punctuation">}</span><span class="token operator">!</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">export</span> <span class="token keyword">default</span> Welcome<span class="token punctuation">;</span> |
Kết hợp các component
Một số component không biết về các component con của nó lúc được tạo ra. Điều này rất phổ biến với những component như Sidebar và Dialog đóng vài trò như là những chiếc “hộp” chung. Trong trường hợp này mình khuyên nên sử dụng props.children để truyền những element con trực tiếp tới phần render của các component này:
1 2 3 4 | <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token operator"><</span>div className<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">'FancyBorder FancyBorder-'</span> <span class="token operator">+</span> props<span class="token punctuation">.</span>color<span class="token punctuation">}</span><span class="token operator">></span> <span class="token punctuation">{</span>props<span class="token punctuation">.</span>children<span class="token punctuation">}</span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> |
Nó giúp cho các component khác truyền những element con một cách linh động hơn bằng cách lồng JSX với nhau:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>MyComponent color<span class="token operator">=</span><span class="token string">"blue"</span><span class="token operator">></span> <span class="token operator"><</span>h1<span class="token operator">></span>Welcome<span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>p<span class="token operator">></span>Thank you <span class="token keyword">for</span> visiting my Website<span class="token operator">!</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>MyComponent<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
đôi khi bạn có thể cần tới nhiều chỗ trống trong một component. Trong trường hợp như thế bạn có thể tạo ra những quy ước của riêng mình thay vì sử dụng 1 children:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"SplitPane"</span><span class="token operator">></span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"SplitPane-left"</span><span class="token operator">></span><span class="token punctuation">{</span>props<span class="token punctuation">.</span>left<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span>div className<span class="token operator">=</span><span class="token string">"SplitPane-right"</span><span class="token operator">></span><span class="token punctuation">{</span>props<span class="token punctuation">.</span>right<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>div<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
sử dụng nó:
1 2 3 4 5 6 7 | <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>SplitPane left<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator"><</span>Contacts <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">}</span> right<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator"><</span>Chat <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Phương pháp này có thể nhắc bạn về khái niệm “slots” trong các thư viện khác nhưng không hề có một giới hạn nào với các tham số có thể truyền như props trong React.
Chuyên biệt hoá
Đôi khi chúng ta nghĩ về các component như là “một trường hợp đặc biệt” của các component khác.
Ví dụ, chúng ta có thể nói rằng WelcomeDialog là một trường hợp đặc biệt của Dialog.
Trong React, ta có thể gộp nhiều component “đặc biệt” để tạo ra một component chung và custom nó với từng props riêng:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">function</span> <span class="token function">Dialog</span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>MyComponent color<span class="token operator">=</span><span class="token string">"blue"</span><span class="token operator">></span> <span class="token operator"><</span>h1 className<span class="token operator">=</span><span class="token string">"Dialog-title"</span><span class="token operator">></span><span class="token punctuation">{</span>props<span class="token punctuation">.</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>p className<span class="token operator">=</span><span class="token string">"Dialog-message"</span><span class="token operator">></span><span class="token punctuation">{</span>props<span class="token punctuation">.</span>message<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>MyComponent<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">WelcomeDialog</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token operator"><</span>Dialog title<span class="token operator">=</span><span class="token string">"Welcome"</span> message<span class="token operator">=</span><span class="token string">"Thank you for visiting our spacecraft!"</span> <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Ví dụ thực tiễn
Ví dụ, một Page component truyền user và avataSize đến một số levels hạ cấp để Link và Avatar components có thể đọc được:
1 2 | <span class="token operator"><</span>Page user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> avatarSize<span class="token operator">=</span><span class="token punctuation">{</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 | <span class="token operator"><</span>PageLayout user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> avatarSize<span class="token operator">=</span><span class="token punctuation">{</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 | <span class="token operator"><</span>NavigationBar user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> avatarSize<span class="token operator">=</span><span class="token punctuation">{</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 3 4 | <span class="token operator"><</span>Link href<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">.</span>permalink<span class="token punctuation">}</span><span class="token operator">></span> <span class="token operator"><</span>Avatar user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> size<span class="token operator">=</span><span class="token punctuation">{</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>Link<span class="token operator">></span> |
mỗi dòng là 1 Component Cha và có call tới Component ngay bên dưới. Bạn có thể cảm thấy dư thừa khi truyền user và avatarSize thông qua nhiều levels nếu chỉ có Avatar component thật sự cần đến nó.
Vấn đề còn lớn hơn nữa nếu Avatar component cần thêm props từ tầng trên cùng, bạn phải thêm tất những props đó ở tất cả những tầng trung gian.
Một cách để khắc phục vấn đề này mà không dùng context là tự truyền Avatar component, bằng cách này các components trung gian không cần phải giữ user hay avataSize props:
1 2 3 4 5 6 7 8 | <span class="token keyword">function</span> <span class="token function">Page</span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> user <span class="token operator">=</span> props<span class="token punctuation">.</span>user<span class="token punctuation">;</span> <span class="token keyword">const</span> userLink <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token operator"><</span>Link href<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">.</span>permalink<span class="token punctuation">}</span><span class="token operator">></span> <span class="token operator"><</span>Avatar user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> size<span class="token operator">=</span><span class="token punctuation">{</span>props<span class="token punctuation">.</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>Link<span class="token operator">></span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token operator"><</span>PageLayout userLink<span class="token operator">=</span><span class="token punctuation">{</span>userLink<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
khi này code sẽ trông như thế này:
1 2 | <span class="token operator"><</span>Page user<span class="token operator">=</span><span class="token punctuation">{</span>user<span class="token punctuation">}</span> avatarSize<span class="token operator">=</span><span class="token punctuation">{</span>avatarSize<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 | <span class="token operator"><</span>PageLayout userLink<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 | <span class="token operator"><</span>NavigationBar userLink<span class="token operator">=</span><span class="token punctuation">{</span><span class="token operator">...</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> |
1 2 | <span class="token punctuation">{</span>props<span class="token punctuation">.</span>userLink<span class="token punctuation">}</span> |
Với cách code này, <Link>
sẽ được tạo tại <Page>
luôn, các Component con không còn biết có thay đổi gì trong <Link>
.
Sự đảo ngược quyền kiểm soát (inversion of control) này có thể giúp code của bạn rõ ràng hơn ở nhiều trường hợp bằng cách giảm số lượng props cần phải truyền xuyên suốt ứng dụng của và cho phép sự kiểm soát đến root component.
Tuy nhiên, đây không phải là một sự lựa chọn tốt cho mọi trường hợp, di chuyển độ phức tạp lên mức cao hơn trong component tree khiến những component ở cấp cao (higher-level components) trở nên phức tạp và buộc cho những component ở mức thấp hơn (lower-level components) trở nên quá linh động.
Đôi khi có những data trùng lặp được sử dụng bởi nhiều components trong component tree, và ở nhiều tầng khác nhau. Lúc này Context cho phép bạn “Phát sóng (broadcast)” những data như vậy, và truyền nó đến tất cả những components bên dưới.
Hiện tại Context đã lỗi thời nên ta có thể sử dụng Redux thay thế