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
export interface WelcomeProps { name: string;
}
export const Welcom = (props: WelcomeProps) = > { return <h1>Hello, {props.name}</h1>;
}
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:
function withdraw(account, amount) { account.total -= amount;
}
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:
<Welcome tenProps1="giatri" tenProps2 = "giatri2"> Giá trị của props.children
</Welcome>
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 :
{ tenProps1: "giatri", tenProps2: "giatri2", children: "Giá trị của props.children" }
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.
import React from "react";
const Welcome = (props) => { console.log(props) //Giá trị của props return ( <div> <h1>Xin chào {props.tenProps1}!</h1> </div> );
};
export default Welcome;
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:
return (<div className={'FancyBorder FancyBorder-' + props.color}> {props.children}
</div>
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:
return ( <MyComponent color="blue"> <h1>Welcome</h1> <p>Thank you for visiting my Website!</p> </MyComponent>
);
đô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:
return ( <div className="SplitPane"> <div className="SplitPane-left">{props.left}</div> <div className="SplitPane-right">{props.right}</div> </div>
);
sử dụng nó:
return ( <SplitPane left={<Contacts />} right={<Chat />} />
);
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:
function Dialog(props) { return ( <MyComponent color="blue"> <h1 className="Dialog-title">{props.title}</h1> <p className="Dialog-message">{props.message}</p> </MyComponent> );
}
function WelcomeDialog() { return (<Dialog title="Welcome" message="Thank you for visiting our spacecraft!" />);
}
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:
<Page user={user} avatarSize={avatarSize} />
<PageLayout user={user} avatarSize={avatarSize} />
<NavigationBar user={user} avatarSize={avatarSize} />
<Link href={user.permalink}> <Avatar user={user} size={avatarSize} />
</Link>
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:
function Page(props) { const user = props.user; const userLink = (<Link href={user.permalink}> <Avatar user={user} size={props.avatarSize} /> </Link>); return <PageLayout userLink={userLink} />;
}
khi này code sẽ trông như thế này:
<Page user={user} avatarSize={avatarSize} />
<PageLayout userLink={...} />
<NavigationBar userLink={...} />
{props.userLink}
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ế