Các loại hook cơ bản trong React(phần 5)

6 min read

Trong phần 5 này, chúng ta sẽ tiếp tục tìm hiểu cách sử dụng hook useContext và useLayoutEffect

1.useContext

useContext trong React Native là một hook được sử dụng để truy cập giá trị của một Context trong functional components. Context giúp truyền dữ liệu giữa các component mà không cần truyền qua nhiều cấp cha-con.

Cách sử dụng

const value = useContext(SomeContext)

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

Hook này được sử dụng trong các trường hợp:

  • Truyền dữ liệu sâu tới tree
  • Cập nhật data được truyền qua context
  • Chỉ định giá trị mặc định dự phòng
  • Ghi đè context cho một phần của tree
  • Tối ưu hóa render lại khi truyền objects và functions.
  • Component của tôi không thấy giá trị từ provider
  • Tôi luôn nhận “undefined” từ context của tôi mặc dù giá trị mặc địch khác

Mục đích sử dụng

  • Truy cập giá trị của Context: Cho phép component con truy cập giá trị được cung cấp bởi Context mà không cần phải truyền qua props từ component cha.
  • Giảm độ sâu của prop drilling: Hữu ích khi giảm bớt sự phức tạp và độ sâu của prop drilling trong ứng dụng.

Lưu ý khi sử dụng useContext

  • Cần phải wrap component cha bằng để cung cấp giá trị cho Context.
  • Context.Provider có thể được sử dụng cho nhiều component con.
  • Nếu giá trị của Context thay đổi, tất cả các component con sẽ được re-render.

Ví dụ minh họa

Ví dụ 1: Truyền dữ liệu sâu tới tree:
Gọi useContext ở top 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 bạn đã chuyển. Để xác định giá trị context, React tìm kiếm component tree và tìm context provider gần nhất ở trên cho context cụ thể đó.

Để chuyển context sang Button, hãy bọc nó hoặc một trong các parent components của nó vào context provider tương ứng:

function MyPage() {

  return (

    <ThemeContext.Provider value="dark">

      <Form />

    </ThemeContext.Provider>

  );

}



function Form() {

  // ... renders buttons inside ...

}

Việc có bao nhiêu lớp components giữa provider và Button. Khi một Button ở bất kỳ vị trí nào bên trong Form gọi useContext(ThemeContext), Button đó sẽ nhận được giá trị “dark“.

Ví dụ 2: Cập nhật data đượ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à chuyển trạng thái 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ờ Button bất kỳ nào bên trong provider sẽ nhận được giá trị của theme hiện tại. Nếu bạn gọi setTheme để cập nhật giá trị cho theme mà bạn chuyển cho provider, tất cả các Button components sẽ render lại với giá trị ‘light‘ mới.

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

Nếu React không thể tìm thấy bất kỳ prodviders của context cụ thể nào trong tree cha, thì giá trị context được trả về bởi useContext() sẽ bằng giá trị mặc định mà bạn đã chỉ định khi 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ị có ý nghĩa hơn mà bạn có thể sử dụng làm giá trị 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ó prodvider tương ứng, nó sẽ không bị hỏng. Điều này cũng giúp các components 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 providers trong các thử nghiệm.

Trong ví dụ bên dưới, nút “Toggle theme” luôn có giá trị “light” vì button này nằm provider của  theme context nào và giá trị mặc định của theme context 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>
  );
}

2.useLayoutEffect

useLayoutEffect trong React Native là một hook tương tự như useEffect, nhưng được thực thi đồng bộ ngay trước khi trình duyệt thực hiện việc repaint (vẽ lại) sau khi render. Nó cung cấp cơ hội để thực hiện các thao tác đồng bộ, như đo lường và tính toán kích thước của các phần tử, trước khi chúng xuất hiện trên màn hình.

Cách sử dụng

useLayoutEffect(setup, dependencies?)

setup: The function với logic Effect của bạn. Nếu component unmount hoặc dependencies thay đổi, hàm cleanup sẽ được gọi để dọn dẹp các công việc side effect không cần thiết.

dependencies: Bao gồm list tất cả các phụ thuộc để tính toán logic bao gồm props, state, và tất cả các biến and functions được khai báo trực tiếp bên trong component của bạn(component body). Nếu các thành phần phụ thuộc này có sự thay đổi thì useEffect mới được chạy lại sau mỗi lần render lại component

Mục đích sử dụng

Đo bố cục ngay trước khi trình duyệt thực hiện việc repaint (vẽ lại) sau khi render
Hầu hết các component không cần biết vị trí và kích thước của chúng trên màn hình để quyết định hiển thị nội dung nào. Chúng chỉ trả lại một số JSX. Sau đó, trình duyệt tính toán bố cục (vị trí và kích thước) của chúng và vẽ lại màn hình.

Đôi khi, điều đó là không đủ. Hãy tưởng tượng một tooltip xuất hiện bên cạnh một số phần tử khi di chuột. Nếu có đủ không gian, tooltip sẽ xuất hiện phía trên phần tử, nhưng nếu không vừa, tooltip sẽ xuất hiện bên dưới. Để hiển thị tooltip ở đúng vị trí cuối cùng, bạn cần biết chiều cao của nó (tức là liệu nó có vừa ở trên cùng hay không).

Để làm điều này, bạn cần render hai lần:

  • Hiển thị tooltip ở bất cứ đâu (ngay cả khi sai vị trí).
  • Đo chiều cao của nó và quyết định vị trí đặt tooltip.
  • Hiển thị lại tooltip ở đúng vị trí.

Tất cả điều này cần phải xảy ra trước khi trình duyệt vẽ lại màn hình. Bạn không muốn người dùng thấy tooltip đang di chuyển. Gọi useLayoutEffect để thực hiện các phép đo bố cục trước khi trình duyệt vẽ lại màn hình:

function Tooltip() {

  const ref = useRef(null);

  const [tooltipHeight, setTooltipHeight] = useState(0); // You don't know real height yet



  useLayoutEffect(() => {

    const { height } = ref.current.getBoundingClientRect();

    setTooltipHeight(height); // Re-render now that you know the real height

  }, []);



  // ...use tooltipHeight in the rendering logic below...

}

Đây là cách hoạt động từng bước:

    • Tooltip renders với khởi tạo chiều cao ban đầu là Height = 0 (vì vậy tooltip có thể bị đặt sai vị trí).
    • React đặt nó vào DOM và chạy mã trong useLayoutEffect.
    • useLayoutEffect của bạn đo chiều cao của nội dung tooltip và kích hoạt render lại ngay lập tức.
    • Tooltip render lại với tooltipHeight thực (để tooltip được định vị chính xác).
    • React cập nhật nó trong DOM và trình duyệt cuối cùng sẽ hiển thị tooltip.

Lưu ý rằng, mặc dù Tooltip Component phải render 2 lần (đầu tiên, với tooltipHeight được khởi tạo bằng 0 và sau đó với chiều cao đo thực), bạn chỉ thấy kết quả cuối cùng. Đây là lý do tại sao bạn cần useLayoutEffect thay vì useEffect cho ví dụ này.

Nguồn dịch: (https://react.dev/reference/react)

Avatar photo

2 Replies to “Các loại hook cơ bản trong React(phần 5)”

Leave a Reply

Your email address will not be published. Required fields are marked *