Trong ứng dụng React, data thường truyền từ trên xuống dưới thông qua props. Nhưng cách này sẽ trở nên phức tạp đối với các loại dữ liệu global như locale, theme…, chúng ta phải truyền chúng qua nhiều lớp component để sử dụng. Context lúc này như vị cứu tinh của React: cung cấp một cách chia sẻ dữ liệu giữa các component như một biến global mà không cần phải truyền props qua mỗi cấp component.
Bạn có cầnsử dụng Context hay không?
Khi nào cần sử dụng Context?
Thông thường, chúng ta sử dụng 2 khái niệm “top-down-data-flow” và “Lifting state up” để truyền dữ liệu từ component cha đến con và cập nhật dữ liệu từ component con lên cha.
Giả sử chúng ta có 3 component A, B, C và lần lượt là con của nhau:
Nếu số lượng component lên đến hàng chục, chúng ta sẽ thấy việc truyền props như thế này sẽ trở nên dài dòng, dư code và khó quản lý. Ngay cả khi những component con trung gian không sử dụng đến props, nó cũng phải nhận props từ component cha để chuyển xuống component con của nó khi cần.
Đó là lý do mà React Context ra đời để giải quyết vấn đề nhập nhằng này!
Ý tưởng của Context là: tập trung lưu dữ liệu (props) ở một nơi. Sau đó React sẽ cung cấp các API để các component có thể lấy props đó trực tiếp mà không cần qua các component trung gian.
Context được thiết kế để chia sẻ dữ liệu như một biến global cho một cây component. Ví dụ các giá trị như authenticated user (thông tin người dùng lúc đăng nhập), theme, languages… được sử dụng thường xuyên ở các tầng React component.
Khi bắt đầu với một dự án dù lớn hay nhỏ, chúng ta nên sử dụng Context từ ban đầu. Sau này có lẽ dự án sẽ phát triển lớn hơn, nhiều component hơn, lúc đó việc truyền props qua từng component sẽ rất phiền phức.
Ưu – nhược điểm của Context
So với luồng quản lý thông thường, Context có những ưu – nhược điểm nhất định.
Ưu điểm
- Giảm thiểu code dư thừa khi một component nhận props nhưng không sử dụng đến.
- Giảm thiểu việc lặp code khi gọi một props cho nhiều component.
- Quản lý dữ liệu ở một nơi giúp việc truy xuất và cập nhật dễ dàng.
Nhược điểm
- Khó tái sử dụng lại component vì dữ liệu tập trung một chỗ.
Cách sử dụng Context (Context API)
React cung cấp các Context API để tạo Context, lấy giá trị của Context cho các loại component:
- React.createContext
- Context.Provider
- Class.contextType
- Context.Consumer
- Context.displayName
React.createContext
API này cho phép khởi tạo một đối tượng Context.
// defaultValue là giá trị được khởi tạo mặc định lúc đầu của MyContext
const MyContext = React.createContext('defaultValue');
Dưới đây là đối tượng Context được khởi tạo, với các thuộc tính Provider, Consumer đi kèm như một component:
Giá trị defaultValue này được sử dụng khi một component không được bọc bởi component Context.Provider. Nó hữu ích cho việc kiểm thử component độc lập mà không cần phải bọc chúng lại.
Context.Provider
Mỗi đối tượng Context đều đi cùng với một Provider như một React component.
<MyContext.Provider value={/* some value */}>{children}</MyContext.Provider>
Component <Provider> nhận một props là “value” để truyền đến component con (có thể được bọc bởi <Consumer> hoặc không). Giá trị của props này không phụ thuộc vào phương thức shouldComponentUpdate của lifecycle updating.
Một component <Provider> có thể chứa nhiều component <Provider> và <Consumer> lồng nhau.
Class.contextType
Thuộc tính contextType được sử dụng cho các class component. Thuộc tính này được gán vào đối tượng Context khai báo bằng React.createContext().
Ta lấy giá trị props “value” của component <Provider> thông qua cú pháp this.context, ta có thể sử dụng nó trong mọi phương thức của lifecycle:
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* ... */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* ... */
}
}
MyClass.contextType = MyContext;
Ngoài ra, chúng ta cũng có thể sử dụng từ khóa static để sử dụng contextType bên trong class component:
const MyContext = React.createContext('defaultValue');
class MyClass extends React.Component {
static contextType = MyContext;
render() {
let value = this.context;
<h1 className={value}>Wiki Tino</h1>
}
}
this.context chỉ trỏ tới một context duy nhất. Giá trị mặc định của nó là đối số “defaultValue” khi khởi tạo Context.
Nếu muốn sử dụng nhiều context khác, chúng ta phải sử dụng “Consuming Multiple Context”.
Context.Consumer
Để lấy giá trị props “value” của component <Provider> trong một function component, ta dùng component Consumer.
Function component nhận giá trị “value” của <Provider> gần nhất trong các tầng component và trả về React element.
const FunctionComponent = (props) => (
<MyContext.Consumer>
{(val) => <span className={val}>Function Component</span>}
</MyContext.Consumer>
);
Nếu MyContext này không có Provider thì tham số val sẽ là đối số defaultValue truyền vào createContext().
Context.displayName
Sau khi một Context được khởi tạo bởi createContext(), nó nhận một thuộc tính displayName kiểu string (chuỗi).
React DevTools sẽ sử dụng chuỗi này để hiển thị lại tên Context. Điều này hữu ích cho việc debug.
const MyContext = React.createContext(/* vài giá trị */);
MyContext.displayName = 'MyDisplayName';
<MyContext.Provider> // "MyDisplayName.Provider" in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" in DevTools
Tổng kết
Chúng ta đã đi tìm hiểu xong khái niệm và các API liên quan đến React Context. Bài viết sau chúng ta sẽ đi vào ví dụ thực tế sử dụng React Context như thế nào nhé !
Cám ơn các bạn đã ghé thăm bài viết này!
Xem bài viết sau tại: ReactJS: Tìm hiểu khái niệm Context (Phần 2)