useContext là một React Hook cho phép bạn đọc và đăng ký context từ component của bạn.

const value = useContext(SomeContext)

Tham khảo

useContext(SomeContext)

Gọi useContext ở cấp cao nhất của component để đọc và đăng ký context.

import { useContext } from 'react';

function MyComponent() {
const theme = useContext(ThemeContext);
// ...

Xem thêm các ví dụ bên dưới.

Tham số

  • SomeContext: Context mà bạn đã tạo trước đó bằng createContext. Bản thân context không chứa thông tin, nó chỉ đại diện cho loại thông tin bạn có thể cung cấp hoặc đọc từ các component.

Giá trị trả về

useContext trả về giá trị context cho component gọi. Nó được xác định là value được truyền cho SomeContext.Provider gần nhất ở trên component gọi trong cây. Nếu không có provider nào như vậy, thì giá trị trả về sẽ là defaultValue mà bạn đã truyền cho createContext cho context đó. Giá trị trả về luôn được cập nhật. React tự động render lại các component đọc một số context nếu nó thay đổi.

Lưu ý

  • Lệnh gọi useContext() trong một component không bị ảnh hưởng bởi các provider được trả về từ cùng component đó. <Context.Provider> tương ứng cần phải ở trên component thực hiện lệnh gọi useContext().
  • React tự động render lại tất cả các phần tử con sử dụng một context cụ thể bắt đầu từ provider nhận được một value khác. Các giá trị trước và tiếp theo được so sánh bằng phép so sánh Object.is. Việc bỏ qua render lại bằng memo không ngăn các phần tử con nhận các giá trị context mới.
  • Nếu hệ thống build của bạn tạo ra các module trùng lặp trong đầu ra (điều này có thể xảy ra với các symlink), điều này có thể phá vỡ context. Việc truyền một thứ gì đó qua context chỉ hoạt động nếu SomeContext mà bạn sử dụng để cung cấp context và SomeContext mà bạn sử dụng để đọc nó là chính xác cùng một đối tượng, như được xác định bằng phép so sánh ===.

Cách sử dụng

Truyền dữ liệu sâu vào cây

Gọi useContext ở cấp cao nhất của component để đọc và đăng ký context.

import { useContext } from 'react';

function Button() {
const theme = useContext(ThemeContext);
// ...

useContext trả về giá trị context cho context mà bạn đã truyền. Để xác định giá trị context, React tìm kiếm cây component và tìm provider context gần nhất ở trên cho context cụ thể đó.

Để truyền context cho một Button, hãy bọc nó hoặc một trong các component cha của nó vào provider context tương ứng:

function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}

function Form() {
// ... renders buttons inside ...
}

Không quan trọng có bao nhiêu lớp component giữa provider và Button. Khi một Button ở bất kỳ đâu bên trong Form gọi useContext(ThemeContext), nó sẽ nhận được "dark" làm giá trị.

Chú Ý

useContext() luôn tìm kiếm provider gần nhất ở trên component gọi nó. Nó tìm kiếm lên trên và không xem xét các provider trong component mà từ đó bạn đang gọi useContext().

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}


Cập nhật dữ liệu được truyền qua context

Thông thường, bạn sẽ muốn context thay đổi theo thời gian. Để cập nhật context, hãy kết hợp nó với state. Khai báo một biến state trong component cha và truyền state hiện tại xuống dưới dạng giá trị context cho provider.

function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}

Bây giờ bất kỳ Button nào bên trong provider sẽ nhận được giá trị theme hiện tại. Nếu bạn gọi setTheme để cập nhật giá trị theme mà bạn truyền cho provider, tất cả các component Button sẽ render lại với giá trị 'light' mới.

Ví dụ về cập nhật context

Example 1 of 5:
Cập nhật một giá trị thông qua context

Trong ví dụ này, component MyApp giữ một biến state sau đó được truyền cho provider ThemeContext. Việc đánh dấu vào ô “Dark mode” sẽ cập nhật state. Thay đổi giá trị được cung cấp sẽ render lại tất cả các component sử dụng context đó.

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={theme}>
      <Form />
      <label>
        <input
          type="checkbox"
          checked={theme === 'dark'}
          onChange={(e) => {
            setTheme(e.target.checked ? 'dark' : 'light')
          }}
        />
        Use dark mode
      </label>
    </ThemeContext.Provider>
  )
}

function Form({ children }) {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}

Lưu ý rằng value="dark" truyền chuỗi "dark", nhưng value={theme} truyền giá trị của biến JavaScript theme với dấu ngoặc nhọn JSX. Dấu ngoặc nhọn cũng cho phép bạn truyền các giá trị context không phải là chuỗi.


Chỉ định giá trị mặc định dự phòng

Nếu React không thể tìm thấy bất kỳ provider nào của context cụ thể đó trong cây cha, giá trị context được trả về bởi useContext() sẽ bằng với giá trị mặc định mà bạn đã chỉ định khi bạn tạo context đó:

const ThemeContext = createContext(null);

Giá trị mặc định không bao giờ thay đổi. Nếu bạn muốn cập nhật context, hãy sử dụng nó với state như đã mô tả ở trên.

Thông thường, thay vì null, có một số giá trị ý nghĩa hơn mà bạn có thể sử dụng làm mặc định, ví dụ:

const ThemeContext = createContext('light');

Bằng cách này, nếu bạn vô tình render một số component mà không có provider tương ứng, nó sẽ không bị hỏng. Điều này cũng giúp các component của bạn hoạt động tốt trong môi trường thử nghiệm mà không cần thiết lập nhiều provider trong các thử nghiệm.

Trong ví dụ dưới đây, nút “Toggle theme” luôn có màu sáng vì nó nằm ngoài bất kỳ theme context provider nào và giá trị theme context mặc định là 'light'. Hãy thử chỉnh sửa theme mặc định thành 'dark'.

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext('light');

export default function MyApp() {
  const [theme, setTheme] = useState('light');
  return (
    <>
      <ThemeContext.Provider value={theme}>
        <Form />
      </ThemeContext.Provider>
      <Button onClick={() => {
        setTheme(theme === 'dark' ? 'light' : 'dark');
      }}>
        Toggle theme
      </Button>
    </>
  )
}

function Form({ children }) {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ children, onClick }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className} onClick={onClick}>
      {children}
    </button>
  );
}


Ghi đè context cho một phần của cây

Bạn có thể ghi đè context cho một phần của cây bằng cách bọc phần đó trong một provider với một giá trị khác.

<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>

Bạn có thể lồng và ghi đè provider bao nhiêu lần tùy thích.

Ví dụ về ghi đè context

Example 1 of 2:
Ghi đè một theme

Ở đây, nút bên trong Footer nhận một giá trị context khác ("light") so với các nút bên ngoài ("dark").

import { createContext, useContext } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button>Sign up</Button>
      <Button>Log in</Button>
      <ThemeContext.Provider value="light">
        <Footer />
      </ThemeContext.Provider>
    </Panel>
  );
}

function Footer() {
  return (
    <footer>
      <Button>Settings</Button>
    </footer>
  );
}

function Panel({ title, children }) {
  const theme = useContext(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      {title && <h1>{title}</h1>}
      {children}
    </section>
  )
}

function Button({ children }) {
  const theme = useContext(ThemeContext);
  const className = 'button-' + theme;
  return (
    <button className={className}>
      {children}
    </button>
  );
}


Tối ưu hóa việc render lại khi truyền các đối tượng và hàm

Bạn có thể truyền bất kỳ giá trị nào qua context, bao gồm cả đối tượng và hàm.

function MyApp() {
const [currentUser, setCurrentUser] = useState(null);

function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}

return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}

Ở đây, giá trị context là một đối tượng JavaScript với hai thuộc tính, một trong số đó là một hàm. Bất cứ khi nào MyApp render lại (ví dụ: khi cập nhật route), đây sẽ là một đối tượng khác trỏ đến một hàm khác, vì vậy React cũng sẽ phải render lại tất cả các component sâu trong cây gọi useContext(AuthContext).

Trong các ứng dụng nhỏ hơn, đây không phải là vấn đề. Tuy nhiên, không cần thiết phải render lại chúng nếu dữ liệu cơ bản, như currentUser, không thay đổi. Để giúp React tận dụng lợi thế đó, bạn có thể bọc hàm login bằng useCallback và bọc việc tạo đối tượng vào useMemo. Đây là một tối ưu hóa hiệu suất:

import { useCallback, useMemo } from 'react';

function MyApp() {
const [currentUser, setCurrentUser] = useState(null);

const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);

const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);

return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}

Do thay đổi này, ngay cả khi MyApp cần render lại, các component gọi useContext(AuthContext) sẽ không cần render lại trừ khi currentUser đã thay đổi.

Đọc thêm về useMemouseCallback.


Khắc phục sự cố

Component của tôi không thấy giá trị từ provider của tôi

Có một vài cách phổ biến mà điều này có thể xảy ra:

  1. Bạn đang render <SomeContext.Provider> trong cùng một component (hoặc bên dưới) với nơi bạn đang gọi useContext(). Di chuyển <SomeContext.Provider> lên trên và ra ngoài component gọi useContext().
  2. Bạn có thể đã quên bọc component của mình bằng <SomeContext.Provider>, hoặc bạn có thể đã đặt nó ở một phần khác của cây so với những gì bạn nghĩ. Kiểm tra xem hệ thống phân cấp có đúng không bằng cách sử dụng React DevTools.
  3. Bạn có thể đang gặp phải một số sự cố build với công cụ của mình khiến SomeContext như được thấy từ component cung cấp và SomeContext như được thấy bởi component đọc là hai đối tượng khác nhau. Điều này có thể xảy ra nếu bạn sử dụng symlink, chẳng hạn. Bạn có thể xác minh điều này bằng cách gán chúng cho các biến toàn cục như window.SomeContext1window.SomeContext2 và sau đó kiểm tra xem window.SomeContext1 === window.SomeContext2 trong console hay không. Nếu chúng không giống nhau, hãy khắc phục sự cố đó ở cấp độ công cụ build.

Tôi luôn nhận được undefined từ context của mình mặc dù giá trị mặc định khác

Bạn có thể có một provider không có value trong cây:

// 🚩 Không hoạt động: không có prop value
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>

Nếu bạn quên chỉ định value, nó giống như truyền value={undefined}.

Bạn cũng có thể đã nhầm lẫn sử dụng một tên prop khác do nhầm lẫn:

// 🚩 Không hoạt động: prop phải được gọi là "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>

Trong cả hai trường hợp này, bạn sẽ thấy cảnh báo từ React trong console. Để khắc phục chúng, hãy gọi prop là value:

// ✅ Truyền prop value
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>

Lưu ý rằng giá trị mặc định từ lệnh gọi createContext(defaultValue) của bạn chỉ được sử dụng nếu hoàn toàn không có provider phù hợp nào ở trên. Nếu có một component <SomeContext.Provider value={undefined}> ở đâu đó trong cây cha, component gọi useContext(SomeContext) sẽ nhận undefined làm giá trị context.