Context
Context cung cấp phương pháp truyền data xuyên suốt component tree mà không cần phải truyền props một cách thủ công qua từng level.
Thông thường với một ứng dụng React, data được truyền từ trên xuống (cha tới con) thông qua props, điều này có vẻ khá cồng kềnh đối với một số loại props (Ví dụ như locale preference, UI theme) chúng thường được sử dụng bởi rất nhiều component trong ứng dụng.
Context cung cấp một cách làm cho phép chúng ta chia sẽ values giống như vậy giữa các components mà không cần truyền giá trị tới tất cả level trong component tree.
useContext là một hooks trong React Hooks cho phép chúng ta có thể làm việc với React Context trong một functional component.
Khi nào nên dùng Context
Context được thiết kế để chia sẻ data khi chúng được xem là “global data” của toàn bộ ứng dụng React, chẳng hạn như thông tin về user hiện tại đang đăng nhập, theme, hoặc ngôn ngữ mà người dùng đã chọn.
Trước khi bạn sử dụng Context
Context chủ yếu được sử dụng khi một số data cần được truy cập bởi nhiều components ở nhiều tầng khác nhau. Sử dụng nó một cách cẩn thận bởi vì nó sẽ làm component trở nên khó tái sử dụng hơn.
Nếu bạn chỉ muốn dùng context để tránh việc truyền một số props qua nhiều levels,
component composition (Kết hợp các component) thường là một giải pháp đơn giản hơn so với context.
Có thể bạn sẽ tìm được đáp án ở đây
Thêm nữa, những gì Context làm được, Redux còn làm tốt hơn, cho nên Context hiện tại rất hiếm gặp, nhưng nếu bạn đang muốn hiểu rõ về Context thì hãy đọc tiếp.
Thực chiến
Tại sao lại thực chiến trước, chưa cả nói lý thuyết gì mà?
Tại vì nhìn code trước có thể sẽ dễ hiểu hơn. Cộng thêm Context hiện tại đã Quá Cũ rồi, mình không muốn update lại bài viết này hay bỏ thêm thời gian để tìm hiểu về nó nữa, nên vào vấn đề chính có lẽ sẽ bớt những câu hỏi tại sao hơn
tạo và sử dụng Context trong ứng dụng React:
tạo file CountProvider.tsx:
import React, { ReactNode, useEffect, useState } from "react";
export const CountContext = React.createContext({count: 0, setCount: (input: number) => {}});
interface CountProviderProps { children: ReactNode;
}
const CountProvider = (props: CountProviderProps) => { const [count, setCount] = useState<number>(0); useEffect(() => { //edit initial value setCount(0); }, []); return (<CountContext.Provider value={{count, setCount}}> { props.children } </CountContext.Provider>);
}
export default CountProvider;
trong file này khai báo và export CountProvider + CountContext
- CountProvider sẽ được sử dụng để cung cấp CountContext cho App hoặc index
- CountContext được sử dụng trong các Component con khi sử dụng và thay đổi Context. Giá trị count được đặt trong useState giúp giá trị được duy trì liên tục
thêm CountProvider vào root Component:
Cho vào Component nào không quan trọng, miễn là nó bao toàn bộ các Component có sử dụng nó là được, mình chọn index.tsx:
import CountProvider from './CountContext';
const container = document.getElementById('root')!;
const root = createRoot(container);
root.render( <CountProvider> <App /> </CountProvider>
);
Nếu có nhiều Provider thì để chúng nó thành con của nhau
sử dụng Context trong các Component con:
import { CountContext } from './CountContext';
const App: React.FC = () => { const CountProvider = useContext(CountContext); useEffect(() => { setTimeout(() => { alert('time is up!') CountProvider.setCount(1); }, 3000); }); return ( <div className="App"> {CountProvider.count} <Router /> </div> );
}
Kiểm tra thành quả:
khi hiển thị màn hình, giá trị mặc định của CountProvider.count là: 0
sau 3 giây, setTimeout được gọi, alert hiển thị và giá trị thành: 1
Điều này có nghĩa là Context đã được áp dụng thành công và có thể update giá trị trong Context
Dưới đây là 1 số kiến thức mà mình thu thập được, cũng không hiểu lắm nhưng đáng để đọc.
React.createContext
Tạo một Context object: const MyContext = React.createContext(defaultValue);
Khi React render một component mà nó subcribe đến Context object này, nó sẽ đọc giá trị hiện tại của context đó từ Provider gần nhất trên component tree.
Đối số defaultValue CHỈ sử dụng khi một component không có Provider nào bên trên nó trong component tree.
Điều này có thể hữu dụng cho việc kiểm thử component một cách cô lập mà không cần phải wrap chúng lại.
==>Lưu ý: truyền undefined như một giá trị Provider sẽ không khiến consuming components sử dụng defaultValue.
Mỗi đối tượng context khi được tạo ra chứa 2 thành phần quan trọng là provider và consumer.
- Provider dùng để cung cấp giá trị
- Consumer nhận về giá trị của context.
đối với Functional Component thì Provider thôi là được rồi
Context.Provider
<MyContext.Provider value={/* some value */}> <App />
</ MyContext.Provider>
Mỗi Context đi cùng với một Provider cho phép Component Con theo dõi sự thay đổi của context đó.
Provider nhận một [value] prop để truyền đến Component Con của Provider này.
Một Provider có thể kết nối đến nhiều Component Con.
Providers có thể lồng nhau để ghi đè giá trị sâu hơn trong component tree.
Nhắc lại: nó sẽ đọc giá trị hiện tại của context đó từ Provider gần nhất trên component tree.
Tất cả consumers con của một Provider sẽ được re-rerender bất cứ khi nào value của Provider đó thay đổi.
Sự lan truyền từ Provider đến consumer con của nó (bao gồm .contextType và useContext) không bị lệ thuộc vào shouldComponentUpdate method, vì vậy consumer được cập nhật ngay cả khi một component cha thoát ra khỏi sự cập nhật đó.
Những thay đổi được xác định bằng cách so sánh những giá trị mới và cũ sử dụng chung một thuật toán như Object.is
=> giá trị của value nên cho vào State và có hàm setState riêng, hàm này sẽ được sử dụng trong các Component con
Class.contextType
==>đây là cách code cho Class Component, hiện tại chỉ nói về Functional Component nhưng Functional Component lại không có contextType nữa nên vẫn cho vào đây contextType là một thuộc tính của class được tạo bởi React.createContext() được dùng để lấy giá trị của context. Cách dùng như sau:
class MyClass extends React.Component { render() { let value = this.context; //giá trị từ provider}
}
MyClass.contextType = MyContext;
Nhờ có gán contextType cho class mà code bên trong class mới dùng được các giá trị trong context.
Context.Consumer
trong Functional Component thì dùng useContext là đủ rồi:
import React, { useContext } from 'react';
import MyContext from './MyContext';
const HomePage = () => { const user = useContext(MyContext); console.log(user); // { name: 'Ustat', age: 24 } return null;
}
Khi một React component subcribe tới sự thay đổi của context. Điều này cho phép bạn subcribe tới một context trong một function component. Yêu cầu một function as a child. Function nhận giá trị context hiện tại và trả về một React node. Tham số value truyền đến function sẽ bằng với value prop của Provider gần nhất trong context này trên tree component. Nếu không có Provider nào cho context này ở trên nó, tham số value sẽ bằng với defaultValue đã được truyền tới createContext().