15 Custom Hooks thường sử dụng trong các dự án với ReactJS

15 Custom Hooks thường sử dụng trong các dự án với ReactJS

Front-end

React custom hooks cho phép tái sử dụng logic trong các component chức năng, tạo điều kiện cho việc tách biệt các component và giữ các phần nhỏ và tập trung vào mục đích cụ thể của chúng. Các custom hooks cũng giúp việc kiểm thử và hiểu logic của một component trở nên dễ dàng hơn, vì nó có thể được cô lập và kiểm thử một cách riêng biệt so với component chính.

Custom hooks cũng giúp việc chia sẻ logic giữa các component khác nhau trở nên dễ dàng hơn, giảm thiểu sự trùng lặp mã và làm cho việc bảo trì và cập nhật mã nguồn trở nên thuận tiện hơn.

I. Các custom hooks thường sử dụng trong ReactJS

1. useToggle

```jsx title="useToggle.jsx" import { useState } from 'react';

export default function useToggle(defaultValue) { const [value, setValue] = useState(defaultValue);

function toggleValue(value) { setValue((currentValue) => (typeof value === 'boolean' ? value : !currentValue)); }

return [value, toggleValue]; }


`useToggle` là một custom React hook cho phép một component chuyển đổi giá trị giữa `true``false`. Nó sử dụng hook `useState` để quản lý trạng thái của nó. Đầu tiên, hook nhận một đối số `defaultValue` để khởi tạo trạng thái giá trị. Sau đó, nó trả về một mảng với hai phần tử: giá trị hiện tại và một hàm được gọi là `toggleValue` chuyển đổi giá trị giữa `true``false`. Hàm này nhận một tham số. Nếu tham số là boolean, nó sẽ đặt giá trị thành tham số. Ngược lại, nó sẽ chuyển đổi giá trị hiện tại.

Dưới đây là một ví dụ về cách bạn có thể sử dụng hook này:

```jsx title="Ví dụ với useToggle"
import useToggle from './useToggle';

export default function ToggleComponent() {
  const [value, toggleValue] = useToggle(false);

  return (
    <div>
      <div>{value.toString()}</div>
      <button onClick={toggleValue}>Toggle</button>
      <button onClick={() => toggleValue(true)}>Make True</button>
      <button onClick={() => toggleValue(false)}>Make False</button>
    </div>
  );
}

2. useTimeout

```jsx title="useTimeout.jsx" import { useCallback, useEffect, useRef } from 'react';

export default function useTimeout(callback, delay) { const callbackRef = useRef(callback); const timeoutRef = useRef();

useEffect(() => { callbackRef.current = callback; }, [callback]);

const set = useCallback(() => { timeoutRef.current = setTimeout(() => callbackRef.current(), delay); }, [delay]);

const clear = useCallback(() => { timeoutRef.current && clearTimeout(timeoutRef.current); }, []);

useEffect(() => { set(); return clear; }, [delay, set, clear]);

const reset = useCallback(() => { clear(); set(); }, [clear, set]);

return { reset, clear }; }


`useTimeout` là một custom React hook cho phép một component thiết lập và xóa một timeout. Nó sử dụng các hook `useCallback`, `useEffect`, và `useRef` từ thư viện React. Hook này nhận vào hai đối số: một hàm callback sẽ được gọi sau khoảng thời gian chờ đợi, và một đối số thời gian chờ đợi là thời gian tính bằng mili giây trước khi callback được gọi.

Hook trả về một đối tượng với hai thuộc tính: `reset``clear`, đây là các hàm có thể được sử dụng để thiết lập lại hoặc xóa timeout.

Hook sử dụng hook `useRef` để tạo ra hai tham chiếu: `callbackRef``timeoutRef`. `callbackRef` giữ hàm callback như một giá trị có thể thay đổi, và `timeoutRef` chứa id của timeout được trả về bởi hàm `setTimeout()`.

Hook `useEffect` được sử dụng để đảm bảo rằng `callbackRef.current` luôn có callback mới nhất được truyền vào. Hàm `set` tạo ra một timeout mới bằng cách sử dụng `setTimeout`, gọi hàm callback sau khoảng thời gian chờ đợi đã chỉ định. Hàm `clear` xóa timeout bằng cách sử dụng `clearTimeout`. Sau đó, có một hook `useEffect` khác được sử dụng để thiết lập timeout khi component được mount và loại bỏ nó khi component bị unmount. Hàm `reset` là sự kết hợp của các hàm `clear``set`. Cuối cùng, hook `useCallback` đảm bảo rằng các hàm chỉ được tạo lại khi các dependency của chúng thay đổi.

Dưới đây là một ví dụ về cách bạn có thể sử dụng hook này:

```jsx title="Ví dụ với useTimeout"
import { useState } from 'react';
import useTimeout from './useTimeout';

export default function TimeoutComponent() {
  const [count, setCount] = useState(10);
  const { clear, reset } = useTimeout(() => setCount(0), 1000);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
      <button onClick={clear}>Clear Timeout</button>
      <button onClick={reset}>Reset Timeout</button>
    </div>
  );
}

Hook useTimeout này có thể hữu ích trong nhiều tình huống khác nhau, nơi mà một component cần hoạt động như một độ trễ không thể tránh khỏi. Ví dụ:

  • Một thông báo biến mất sau một khoảng thời gian nhất định.
  • Một biểu mẫu hiển thị một biểu tượng quay vòng trong một khoảng thời gian nhất định trước khi chuyển hướng.
  • Một bản trình chiếu tự động chuyển đến slide tiếp theo sau một khoảng thời gian nhất định.
  • Một đồng hồ đếm ngược hiển thị thời gian còn lại và kích hoạt một hành động khi nó đạt đến số không.
  • Một tính năng tự động lưu dữ liệu biểu mẫu sau một khoảng thời gian nhất định.
  • Một timeout phiên làm việc đăng xuất người dùng sau một khoảng thời gian không hoạt động nhất định.
  • Một hàm debounce trì hoãn việc thực thi callback trong một khoảng thời gian nhất định.
  • Nó có thể được sử dụng trong mọi tình huống mà bạn cần đợi một khoảng thời gian nhất định trước khi thực hiện hành động hoặc để lặp lại một hành động nhiều lần với một độ trễ giữa chúng.

3. useDebounce

```jsx title="useDebounce.jsx" import { useEffect } from 'react'; import useTimeout from '../2-useTimeout/useTimeout';

export default function useDebounce(callback, delay, dependencies) { const { reset, clear } = useTimeout(callback, delay); useEffect(reset, [...dependencies, reset]); useEffect(clear, []); }


`useDebounce` là một React hook tùy chỉnh cho phép một component trì hoãn việc thực thi một hàm callback trong một khoảng thời gian nhất định. Hook này sử dụng hook sẵn có là `useEffect` từ thư viện React và hook tùy chỉnh `useTimeout` (được đề cập ở mục 2).

Hook này có ba đối số:

- `callback` là hàm callback cần được trì hoãn.
- `delay` là thời gian trong miligiây trôi qua trước khi hàm callback được gọi.
- `dependencies` là một mảng các giá trị mà hook nên lắng nghe để theo dõi sự thay đổi và chạy lại hàm callback nếu có bất kỳ sự thay đổi nào.

Hook này sử dụng hook `useTimeout` để tạo một timeout sẽ gọi hàm callback sau khoảng thời gian đã chỉ định. Hook `useEffect` được sử dụng để thiết lập timeout khi component được mount và xóa timeout khi nó được unmount. `useEffect` đầu tiên sẽ gọi hàm `reset` khi bất kỳ phụ thuộc nào thay đổi, và `useEffect` thứ hai sẽ gọi hàm `clear` khi component bị unmount.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useDebounce"
import { useState } from 'react';
import useDebounce from './useDebounce';

export default function DebounceComponent() {
  const [count, setCount] = useState(10);
  useDebounce(() => alert(count), 1000, [count]);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
    </div>
  );
}

Hook này có thể hữu ích trong những tình huống bạn muốn giới hạn số lần hàm callback được gọi trong một khoảng thời gian ngắn. Ví dụ, khi bạn có một trường nhập liệu gửi một yêu cầu tìm kiếm đến máy chủ sau mỗi lần nhấn phím, bạn nên chờ đợi người dùng dừng gõ trước khi gửi yêu cầu để tránh gửi lưu lượng mạng không cần thiết và cải thiện trải nghiệm người dùng.

4. useUpdateEffect

```jsx title="useUpdateEffect.jsx" import { useEffect, useRef } from 'react';

export default function useUpdateEffect(callback, dependencies) { const firstRenderRef = useRef(true);

useEffect(() => { if (firstRenderRef.current) { firstRenderRef.current = false; return; } return callback(); }, dependencies); }


Hook `useUpdateEffect` là một hook React tùy chỉnh cho phép một component chạy một hàm callback chỉ khi các phụ thuộc cụ thể thay đổi. Nó sử dụng các hook `useRef``useEffect` có sẵn trong thư viện React.

Hook này nhận hai đối số:

- `callback` là hàm sẽ được gọi khi các phụ thuộc thay đổi.
- `dependencies` là một mảng các giá trị mà hook sẽ theo dõi để kiểm tra sự thay đổi.

Hook sử dụng `useRef` để tạo một tham chiếu `firstRenderRef` với giá trị khởi tạo là `true`. Tham chiếu này sẽ được sử dụng để theo dõi lần render đầu tiên của component.

Hook `useEffect` được sử dụng để lắng nghe sự thay đổi trong mảng phụ thuộc và gọi hàm callback. Bên trong hàm `useEffect`, nó kiểm tra xem đây có phải là lần render đầu tiên của component hay không bằng cách kiểm tra giá trị của `firstRenderRef`. Nếu đúng, nó đặt giá trị này thành `false` và trả về. Nếu không, điều này có nghĩa là đây là một cập nhật, vì vậy nó sẽ gọi hàm callback và trả về hàm callback.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useUpdateEffect"
import { useState } from 'react';
import useUpdateEffect from './useUpdateEffect';

export default function UpdateEffectComponent() {
  const [count, setCount] = useState(10);
  useUpdateEffect(() => alert(count), [count]);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount((c) => c + 1)}>Increment</button>
    </div>
  );
}

Hook useUpdateEffect có thể hữu ích trong các tình huống khi bạn muốn chạy một số logic chỉ khi giá trị cụ thể thay đổi và không chạy trong lần render ban đầu. Ví dụ, khi bạn muốn lấy dữ liệu từ một API sau khi người dùng đã chọn một tùy chọn cụ thể từ menu thả xuống hoặc khi bạn muốn cập nhật vị trí của một phần tử trên màn hình sau khi kích thước của cửa sổ thay đổi.

5. useArray

```jsx title="useArray.jsx" import { useState } from 'react';

export default function useArray(defaultValue) { const [array, setArray] = useState(defaultValue);

function push(element) { setArray((a) => [...a, element]); }

function filter(callback) { setArray((a) => a.filter(callback)); }

function update(index, newElement) { setArray((a) => [...a.slice(0, index), newElement, ...a.slice(index + 1, a.length)]); }

function remove(index) { setArray((a) => [...a.slice(0, index), ...a.slice(index + 1, a.length)]); }

function clear() { setArray([]); }

return { array, set: setArray, push, filter, update, remove, clear }; }


Hook `useArray` là một custom React hook cho phép một component quản lý trạng thái của một mảng. Nó sử dụng hook `useState` tích hợp sẵn từ thư viện React. Hook này nhận một đối số là `defaultValue`, được sử dụng để khởi tạo trạng thái của mảng. Hook trả về một đối tượng với một số thuộc tính:

- `array` là trạng thái hiện tại của mảng.
- `set` là một hàm cho phép bạn đặt trạng thái của mảng thành một giá trị mới.
- `push` là một hàm sẽ cho phép bạn thêm một phần tử vào cuối mảng.
- `filter` là một hàm cho phép bạn lọc mảng bằng cách truyền một hàm gọi lại.
- `update` là một hàm sẽ cho phép bạn cập nhật một phần tử tại một chỉ mục cụ thể của mảng.
- `remove` là một hàm sẽ cho phép bạn loại bỏ một phần tử tại một chỉ mục cụ thể của mảng.
- `clear` là một hàm sẽ cho phép bạn xóa sạch mảng.

Tất cả các hàm thay đổi trạng thái của mảng đều sử dụng hàm `setArray`, nhưng nó được thực hiện sao cho bảo toàn tính không thay đổi của trạng thái bằng cách tạo một mảng mới, thêm hoặc loại bỏ các phần tử, sau đó chuyển nó vào hàm `setArray`.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useArray"
import useArray from './useArray';

export default function ArrayComponent() {
  const { array, set, push, remove, filter, update, clear } = useArray([1, 2, 3, 4, 5, 6]);

  return (
    <div>
      <div>{array.join(', ')}</div>
      <button onClick={() => push(7)}>Add 7</button>
      <button onClick={() => update(1, 9)}>Change Second Element To 9</button>
      <button onClick={() => remove(1)}>Remove Second Element</button>
      <button onClick={() => filter((n) => n < 3)}>Keep Numbers Less Than 4</button>
      <button onClick={() => set([1, 2])}>Set To 1, 2</button>
      <button onClick={clear}>Clear</button>
    </div>
  );
}

Hook useArray có thể hữu ích trong những tình huống mà bạn muốn quản lý một mảng dữ liệu trong trạng thái của một component và thực hiện các hoạt động mảng như thêm, loại bỏ, cập nhật và lọc các phần tử.

6. usePrevious

```jsx title="usePrevious.jsx" import { useRef } from 'react';

export default function usePrevious(value) { const currentRef = useRef(value); const previousRef = useRef();

if (currentRef.current !== value) { previousRef.current = currentRef.current; currentRef.current = value; }

return previousRef.current; }


Hook `usePrevious` là một hook tùy chỉnh trong React cho phép một component theo dõi giá trị trước đó của một biến. Nó sử dụng hook `useRef` tích hợp sẵn từ thư viện React.

Hook này nhận một đối số là giá trị hiện tại của biến. Sau đó, nó tạo ra hai refs, một cái được gọi là `currentRef`, giữ giá trị hiện tại của biến, và một cái khác được gọi là `previousRef`, giữ giá trị trước đó của biến.

Hook so sánh giá trị hiện tại với giá trị trước đó. Nếu chúng khác nhau, nó cập nhật `previousRef` với giá trị hiện tại và `currentRef` với giá trị mới. Sau đó, nó trả về `previousRef.current`.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với usePrevious"
import { useState } from 'react';
import usePrevious from './usePrevious';

export default function PreviousComponent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('Kyle');
  const previousCount = usePrevious(count);

  return (
    <div>
      <div>
        {count} - {previousCount}
      </div>
      <div>{name}</div>
      <button onClick={() => setCount((currentCount) => currentCount + 1)}>Increment</button>
      <button onClick={() => setName('John')}>Change Name</button>
    </div>
  );
}

Hook này có thể hữu ích trong những tình huống bạn cần truy cập vào giá trị trước đó của một biến, ví dụ như khi bạn muốn so sánh giá trị hiện tại với giá trị trước đó để kiểm tra xem nó có thay đổi hay không, hoặc khi bạn muốn theo dõi sự thay đổi của một biến theo thời gian.

7. useStateWithHistory

```jsx title="useStateWithHistory.jsx" import { useCallback, useRef, useState } from 'react';

export default function useStateWithHistory(defaultValue, { capacity = 10 } = {}) { const [value, setValue] = useState(defaultValue); const historyRef = useRef([value]); const pointerRef = useRef(0);

const set = useCallback( (v) => { const resolvedValue = typeof v === 'function' ? v(value) : v; if (historyRef.current[pointerRef.current] !== resolvedValue) { if (pointerRef.current < historyRef.current.length - 1) { historyRef.current.splice(pointerRef.current + 1); } historyRef.current.push(resolvedValue);

while (historyRef.current.length > capacity) { historyRef.current.shift(); } pointerRef.current = historyRef.current.length - 1; } setValue(resolvedValue); }, [capacity, value], );

const back = useCallback(() => { if (pointerRef.current <= 0) return; pointerRef.current--; setValue(historyRef.current[pointerRef.current]); }, []);

const forward = useCallback(() => { if (pointerRef.current >= historyRef.current.length - 1) return; pointerRef.current++; setValue(historyRef.current[pointerRef.current]); }, []);

const go = useCallback((index) => { if (index

historyRef.current.length - 1) return; pointerRef.current = index; setValue(historyRef.current[pointerRef.current]); }, []);

return [ value, set, { history: historyRef.current, pointer: pointerRef.current, back, forward, go, }, ]; }


`useStateWithHistory` là một hook tùy chỉnh của React cho phép một component theo dõi lịch sử trạng thái. Nó sử dụng những hook có sẵn như `useState`, `useCallback``useRef` từ thư viện React.

Hook này nhận vào hai đối số:

- `defaultValue` là giá trị ban đầu của trạng thái.
- `capacity` là một đối số tùy chọn để đặt số lượng tối đa các trạng thái nên được lưu trữ trong lịch sử.

Hook tạo ra hai refs, một được gọi là `historyRef` giữ một mảng của lịch sử trạng thái, và một cái khác được gọi là `pointerRef` giữ con trỏ hiện tại của lịch sử. Nó cũng tạo ra ba hàm gọi lại: `set`, `back`, và `forward`.

- Hàm `set` được sử dụng để đặt trạng thái, nó hoạt động tương tự như hàm setState có sẵn, nhưng nó cũng theo dõi lịch sử trạng thái bằng cách thêm giá trị mới vào mảng lịch sử và cập nhật `pointerRef`. Hàm này có thể nhận một giá trị hoặc một hàm gọi lại nhận trạng thái hiện tại làm đối số. Hàm cũng đảm bảo rằng dung lượng của mảng lịch sử không bị vượt quá bằng cách loại bỏ phần tử cũ nhất.
- Hàm `back` di chuyển đến trạng thái trước đó trong lịch sử. Nó giảm giá trị của `pointerRef` và cập nhật trạng thái với giá trị trước đó của mảng lịch sử.
- Hàm `forward` di chuyển đến trạng thái tiếp theo trong lịch sử. Nó tăng giá trị của `pointerRef` và cập nhật trạng thái với giá trị tiếp theo trong mảng lịch sử.
- Hàm `go` di chuyển đến một trạng thái cụ thể trong lịch sử. Nó đặt giá trị của `pointerRef` thành chỉ số được truyền làm đối số và cập nhật trạng thái với giá trị tại chỉ mục đó trong mảng lịch sử.

Hook trả về một mảng với hai phần tử:

- Giá trị trạng thái hiện tại
- Một đối tượng chứa mảng lịch sử, con trỏ và các hàm `set`, `back`, `forward`, và `go`.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useStateWithHistory"
import { useState } from 'react';
import useStateWithHistory from './useStateWithHistory';

export default function StateWithHistoryComponent() {
  const [count, setCount, { history, pointer, back, forward, go }] = useStateWithHistory(1);
  const [name, setName] = useState('Kyle');

  return (
    <div>
      <div>{count}</div>
      <div>{history.join(', ')}</div>
      <div>Pointer - {pointer}</div>
      <div>{name}</div>
      <button onClick={() => setCount((currentCount) => currentCount * 2)}>Double</button>
      <button onClick={() => setCount((currentCount) => currentCount + 1)}>Increment</button>
      <button onClick={back}>Back</button>
      <button onClick={forward}>Forward</button>
      <button onClick={() => go(2)}>Go To Index 2</button>
      <button onClick={() => setName('John')}>Change Name</button>
    </div>
  );
}

Hook này có thể hữu ích trong những tình huống khi bạn muốn theo dõi lịch sử trạng thái, ví dụ như khi bạn muốn triển khai chức năng undo hoặc redo hoặc cho phép người dùng điều hướng qua lịch sử các thay đổi.

8. useStorage

```jsx title="useStorage.jsx" import { useCallback, useState, useEffect } from 'react';

export function useLocalStorage(key, defaultValue) { return useStorage(key, defaultValue, window.localStorage); }

export function useSessionStorage(key, defaultValue) { return useStorage(key, defaultValue, window.sessionStorage); }

function useStorage(key, defaultValue, storageObject) { const [value, setValue] = useState(() => { const jsonValue = storageObject.getItem(key); if (jsonValue != null) return JSON.parse(jsonValue);

if (typeof defaultValue === 'function') { return defaultValue(); } else { return defaultValue; } });

useEffect(() => { if (value === undefined) return storageObject.removeItem(key); storageObject.setItem(key, JSON.stringify(value)); }, [key, value, storageObject]);

const remove = useCallback(() => { setValue(undefined); }, []);

return [value, setValue, remove]; }


`useLocalStorage``useSessionStorage` là những hook tùy chỉnh của React cho phép một component lưu trữ một giá trị trong `LocalStorage` hoặc `SessionStorage` của trình duyệt và đồng bộ nó với trạng thái của component. Hook này sử dụng các hook có sẵn là `useState``useEffect` từ thư viện React cũng như hook `useCallback`.

Hai hàm `useLocalStorage``useSessionStorage` tương tự nhau nhưng sử dụng hai loại lưu trữ khác nhau là `localStorage``sessionStorage`. Cả hai hàm này nhận vào hai tham số là `key``defaultValue`. `key` là khóa được sử dụng để lưu trữ giá trị trong đối tượng lưu trữ, và `defaultValue` là giá trị sẽ được sử dụng nếu key không được tìm thấy trong đối tượng lưu trữ.

Cả hai hàm sử dụng hàm `storage`, nhận vào ba tham số: `key`, `defaultValue`, và `storageObject` và trả về một mảng gồm ba phần tử:

- Giá trị hiện tại.
- Một hàm `setValue` có thể được sử dụng để cập nhật giá trị trong trạng thái và lưu trữ.
- Một hàm `remove` có thể được sử dụng để loại bỏ giá trị khỏi trạng thái và lưu trữ.

Hook `useEffect` giữ cho giá trị được lưu trữ trong lưu trữ của trình duyệt được đồng bộ với trạng thái của component.

Hàm `useStorage` sử dụng các phương thức `JSON.stringify()``JSON.parse` để chuyển đổi giá trị thành một chuỗi JSON khi lưu trữ nó trong đối tượng lưu trữ và chuyển đổi nó lại thành một đối tượng JavaScript khi lấy nó từ đối tượng lưu trữ. Điều này cho phép hook làm việc với bất kỳ dữ liệu nào, không chỉ là chuỗi.

Hook `useEffect` chạy mỗi khi `key`, `value`, hoặc `storageObject` thay đổi. Trước tiên, nó kiểm tra xem giá trị có phải là `undefined` không. Trong trường hợp đó, nó sẽ loại bỏ mục từ đối tượng lưu trữ. Ngược lại, nó sẽ lưu trữ giá trị trong đối tượng lưu trữ.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useStorage"
import { useSessionStorage, useLocalStorage } from './useStorage';

export default function StorageComponent() {
  const [name, setName, removeName] = useSessionStorage('name', 'Kyle');
  const [age, setAge, removeAge] = useLocalStorage('age', 26);

  return (
    <div>
      <div>
        {name} - {age}
      </div>
      <button onClick={() => setName('John')}>Set Name</button>
      <button onClick={() => setAge(40)}>Set Age</button>
      <button onClick={removeName}>Remove Name</button>
      <button onClick={removeAge}>Remove Age</button>
    </div>
  );
}

Hook này có thể hữu ích trong các tình huống bạn muốn lưu trữ dữ liệu qua các phiên làm việc hoặc trang của trình duyệt và duy trì đồng bộ dữ liệu với trạng thái của component. Ví dụ, bạn có thể lưu trữ cài đặt người dùng, dữ liệu của một biểu mẫu hoặc một danh sách công việc cần làm. Việc sử dụng các hook useLocalStorageuseSessionStorage mang lại linh hoạt khi bạn muốn sử dụng lưu trữ local hoặc lưu trữ phiên của trình duyệt tùy thuộc vào yêu cầu cụ thể.

9. useAsync

```jsx title="useAsync.jsx" import { useCallback, useEffect, useState } from 'react';

export default function useAsync(callback, dependencies = []) { const [loading, setLoading] = useState(true); const [error, setError] = useState(); const [value, setValue] = useState();

const callbackMemoized = useCallback(() => { setLoading(true); setError(undefined); setValue(undefined); callback() .then(setValue) .catch(setError) .finally(() => setLoading(false)); }, dependencies);

useEffect(() => { callbackMemoized(); }, [callbackMemoized]);

return { loading, error, value }; }


`useAsync` là một hook tùy chỉnh trong React, cho phép một component xử lý các hoạt động bất đồng bộ và theo dõi trạng thái `loading`, `error`, và `value`. Nó sử dụng các hook có sẵn như `useState``useEffect` từ thư viện React cùng với hook `useCallback`.

Hook này nhận vào hai đối số:

- `callback` là một hàm trả về một promise. Hàm này chịu trách nhiệm thực hiện hoạt động bất đồng bộ.
- `dependencies` là một mảng các `dependencies` mà hook nên theo dõi sự thay đổi. Hàm callback sẽ được thực thi khi bất kỳ `dependencies` nào thay đổi.

`useAsync` tạo ra ba biến trạng thái: `loading`, `error`, và `value`. Trạng thái `loading` được sử dụng để chỉ ra xem hoạt động bất đồng bộ có đang diễn ra hay không, trạng thái `error` để lưu trữ đối tượng lỗi trong trường hợp promise bị từ chối, và trạng thái `value` để lưu trữ giá trị được giải quyết trong trường hợp promise được thực hiện.

Hook cũng tạo ra một hàm gọi là `callbackMemoized` bằng cách sử dụng `useCallback`. Hàm này thiết lập các trạng thái `loading`, `error`, và `value` về các giá trị khởi tạo của chúng và sau đó gọi hàm callback được truyền vào.

`useEffect` gọi hàm `callbackMemoized` khi các `dependencies` thay đổi.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useAsync"
import useAsync from './useAsync';

export default function AsyncComponent() {
  const { loading, error, value } = useAsync(() => {
    return new Promise((resolve, reject) => {
      const success = false;
      setTimeout(() => {
        success ? resolve('Hi') : reject('Error');
      }, 1000);
    });
  });

  return (
    <div>
      <div>Loading: {loading.toString()}</div>
      <div>{error}</div>
      <div>{value}</div>
    </div>
  );
}

Hook này có thể hữu ích trong các tình huống mà bạn muốn xử lý các hoạt động bất đồng bộ như lấy dữ liệu từ một API, tải lên một tệp, hoặc lưu dữ liệu vào cơ sở dữ liệu. Nó cung cấp một cách đơn giản để quản lý trạng thái loading, error, và value trong một component, đồng thời cũng cho phép component chạy lại hoạt động bất đồng bộ khi một số giá trị cụ thể thay đổi.

10. useFetch

```jsx title="useFetch.jsx" import useAsync from '../9-useAsync/useAsync';

const DEFAULT_OPTIONS = { headers: { 'Content-Type': 'application/json' }, };

export default function useFetch(url, options = {}, dependencies = []) { return useAsync(() => { return fetch(url, { ...DEFAULT_OPTIONS, ...options }).then((res) => { if (res.ok) return res.json(); return res.json().then((json) => Promise.reject(json)); }); }, dependencies); }


`useFetch` là một hook tùy chỉnh của React, cho phép một component xử lý việc lấy dữ liệu từ một URL và theo dõi trạng thái `loading`, `error`, và `value`. Nó sử dụng fetch API có sẵn và hook tùy chỉnh `useAsync` để giúp một component xử lý các hoạt động bất đồng bộ và theo dõi trạng thái `loading`, `error`, và `value`.

Hook nhận vào ba tham số:

- `URL` là địa chỉ URL của điểm cuối để lấy dữ liệu.
- `options` là một đối tượng chứa các tùy chọn như `headers`, `method`, và `body` cho yêu cầu fetch.
- `dependencies` là một mảng các phụ thuộc mà hook nên lắng nghe để theo dõi sự thay đổi. Hàm callback sẽ được thực thi khi bất kỳ phụ thuộc nào thay đổi.

Hook tạo ra một hàm gọi là callback sử dụng fetch API để yêu cầu URL cụ thể với các tùy chọn được truyền vào cùng với các tùy chọn mặc định.

Sau đó, nó kiểm tra xem phản hồi có thành công không. Nếu có, nó trả về phản hồi dưới dạng json. Ngược lại, nó trả về phản hồi JSON và reject nó.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useFetch"
import { useState } from 'react';
import useFetch from './useFetch';

export default function FetchComponent() {
  const [id, setId] = useState(1);
  const { loading, error, value } = useFetch(`https://jsonplaceholder.typicode.com/todos/${id}`, {}, [id]);

  return (
    <div>
      <div>{id}</div>
      <button onClick={() => setId((currentId) => currentId + 1)}>Increment ID</button>
      <div>Loading: {loading.toString()}</div>
      <div>{JSON.stringify(error, null, 2)}</div>
      <div>{JSON.stringify(value, null, 2)}</div>
    </div>
  );
}

Hook này có thể hữu ích trong những tình huống mà bạn muốn xử lý việc lấy dữ liệu từ một API. Nó cung cấp một cách đơn giản để quản lý trạng thái loading, error, và value trong một component và cũng cho phép component.

11. useScript

```jsx title="useScript.jsx" import useAsync from '../9-useAsync/useAsync';

export default function useScript(url) { return useAsync(() => { const script = document.createElement('script'); script.src = url; script.async = true;

return new Promise((resolve, reject) => { script.addEventListener('load', resolve); script.addEventListener('error', reject); document.body.appendChild(script); }); }, [url]); }


`useScript` là một hook tùy chỉnh trong React, cho phép một component tải một tệp JavaScript từ một URL cụ thể và theo dõi các trạng thái `loading`, `error`, và `value`. Ngoài ra, nó sử dụng hook tùy chỉnh `useAsync` để xử lý các hoạt động bất đồng bộ và theo dõi các trạng thái `loading`, `error`, và `value`.

Hook này nhận vào một đối số:

- `URL` là URL của tệp JavaScript cần tải.

Hook tạo ra một hàm gọi lại sử dụng DOM API để tạo một phần tử script mới và đặt thuộc tính src của nó thành URL được truyền vào. Nó cũng đặt thuộc tính async thành `true`.

Sau đó, nó trả về một promise mới sẽ được giải quyết hoặc bị từ chối khi script được tải hoặc gặp lỗi.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useScript"
import useScript from './useScript';

export default function ScriptComponent() {
  const { loading, error } = useScript('https://code.jquery.com/jquery-3.6.0.min.js');

  if (loading) return <div>Loading</div>;
  if (error) return <div>Error</div>;
  return <div>{window.$(window).width()}</div>;
}

Hook này có thể hữu ích trong những tình huống bạn muốn tải động các thư viện JavaScript bên ngoài. Nó cung cấp một cách đơn giản để quản lý trạng thái loading, errorvalue của một component và cũng cho phép component tải lại script khi URL thay đổi.

12. useDeepCompareEffect

```jsx title="useDeepCompareEffect.jsx" import { useEffect, useRef } from 'react'; import isEqual from 'lodash/fp/isEqual';

export default function useDeepCompareEffect(callback, dependencies) { const currentDependenciesRef = useRef();

if (!isEqual(currentDependenciesRef.current, dependencies)) { currentDependenciesRef.current = dependencies; }

useEffect(callback, [currentDependenciesRef.current]); }


Hook `useDeepCompareEffect` là một hook tùy chỉnh của React, cho phép một component chạy một hiệu ứng chỉ khi các dependencies đã thay đổi sử dụng `deep comparison` thay vì `shallow comparison`. Hook này sử dụng hook `useEffect` tích hợp sẵn từ thư viện React và hàm `isEqual` của `lodash` để thực hiện `deep comparison`.

Hook này nhận hai đối số:

- `callback`: Là một hàm biểu diễn hiệu ứng sẽ được thực hiện.
- `dependencies`: Là một mảng các giá trị mà hiệu ứng phụ thuộc vào.

Nó cũng tạo một ref được gọi là `currentDependenciesRef` để lưu trữ các dependencies hiện tại.

Sau đó, nó so sánh các dependencies hiện tại với các dependencies mới bằng cách sử dụng hàm `isEqual`. Nếu chúng không bằng nhau, nó cập nhật ref dependencies hiện tại với các dependencies mới.

Sau đó, nó gọi `useEffect` với hàm callback và `currentDependenciesRef.current` như là dependencies.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useDeepCompareEffect"
import { useEffect, useState, useRef } from 'react';
import useDeepCompareEffect from './useDeepCompareEffect';

export default function DeepCompareEffectComponent() {
  const [age, setAge] = useState(0);
  const [otherCount, setOtherCount] = useState(0);
  const useEffectCountRef = useRef();
  const useDeepCompareEffectCountRef = useRef();

  const person = { age: age, name: 'Kyle' };

  useEffect(() => {
    useEffectCountRef.current.textContent = parseInt(useEffectCountRef.current.textContent) + 1;
  }, [person]);

  useDeepCompareEffect(() => {
    useDeepCompareEffectCountRef.current.textContent = parseInt(useDeepCompareEffectCountRef.current.textContent) + 1;
  }, [person]);

  return (
    <div>
      <div>
        useEffect: <span ref={useEffectCountRef}>0</span>
      </div>
      <div>
        useDeepCompareEffect: <span ref={useDeepCompareEffectCountRef}>0</span>
      </div>
      <div>Other Count: {otherCount}</div>
      <div>{JSON.stringify(person)}</div>
      <button onClick={() => setAge((currentAge) => currentAge + 1)}>Increment Age</button>
      <button onClick={() => setOtherCount((count) => count + 1)}>Increment Other Count</button>
    </div>
  );
}

Hook useDeepCompareEffect có thể hữu ích trong những tình huống nơi các dependencies là các đối tượng hoặc mảng phức tạp, và bạn muốn đảm bảo rằng hiệu ứng chỉ chạy khi các giá trị cụ thể bên trong dependencies đã thay đổi. Nó có thể giúp ngăn chặn các lần render không cần thiết và cải thiện hiệu suất.

13. useEventListener

```jsx title="useEventListener.jsx" import { useEffect, useRef } from 'react';

export default function useEventListener(eventType, callback, element = window) { const callbackRef = useRef(callback);

useEffect(() => { callbackRef.current = callback; }, [callback]);

useEffect(() => { if (element == null) return; const handler = (e) => callbackRef.current(e); element.addEventListener(eventType, handler);

return () => element.removeEventListener(eventType, handler); }, [eventType, element]); }


Hook `useEventListener` là một hook tùy chỉnh của React, cho phép một component thêm một trình nghe sự kiện vào một phần tử DOM cụ thể và thực hiện một hàm gọi lại khi sự kiện xảy ra. Nó sử dụng hook `useEffect` có sẵn từ thư viện React.

Hook này có ba đối số:

- `eventType` là một chuỗi đại diện cho loại sự kiện cần lắng nghe, như `click` hoặc `keydown`.
- `callback` là một hàm đại diện cho hành động sẽ được thực hiện khi sự kiện xảy ra.
- `element` là một phần tử DOM tùy chọn để thêm trình lắng nghe sự kiện. Giá trị mặc định là window, có nghĩa là trình lắng nghe sự kiện sẽ được thêm vào đối tượng cửa sổ toàn cầu.

Nó cũng tạo một ref được gọi là `callbackRef` để lưu trữ hàm gọi lại hiện tại.

Hook `useEffect` được sử dụng để thiết lập trình lắng nghe sự kiện khi component được gắn kết và để loại bỏ trình lắng nghe sự kiện khi component bị gỡ bỏ. Nó cũng cập nhật `ref` callback khi hàm gọi lại thay đổi.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useEventListener"
import { useState } from 'react';
import useEventListener from './useEventListener';

export default function EventListenerComponent() {
  const [key, setKey] = useState('');
  useEventListener('keydown', (e) => {
    setKey(e.key);
  });

  return <div>Last Key: {key}</div>;
}

Hook useEventListener có thể hữu ích trong những tình huống bạn muốn xử lý các sự kiện như clicks, key presses, hoặc submissions của form một cách mô tả và giữ logic của component của bạn tách biệt với logic xử lý sự kiện.

14. useOnScreen

```jsx title="useOnScreen.jsx" import { useEffect, useState } from 'react';

export default function useOnScreen(ref, rootMargin = '0px') { const [isVisible, setIsVisible] = useState(false);

useEffect(() => { if (ref.current == null) return; const observer = new IntersectionObserver(([entry]) => setIsVisible(entry.isIntersecting), { rootMargin }); observer.observe(ref.current); return () => { if (ref.current == null) return; observer.unobserve(ref.current); }; }, [ref.current, rootMargin]);

return isVisible; }


Hook `useOnScreen` là một hook tùy chỉnh trong React cho phép một component phát hiện khi một phần tử cụ thể trong DOM trở nên hiển thị trong khung nhìn và theo dõi trạng thái sự hiển thị. Nó sử dụng hook `useEffect` tích hợp sẵn trong thư viện React và `API IntersectionObserver`.

Hook nhận hai đối số:

- `ref`: Tham chiếu đến phần tử DOM mà bạn muốn theo dõi sự hiển thị, thường được tạo bằng cách sử dụng hook useRef trong React.
- `rootMargin`: Một chuỗi tùy chọn định nghĩa một lề xung quanh phần tử gốc. Nó có thể được sử dụng để mở rộng hoặc thu nhỏ hộp giới hạn của phần tử gốc trước khi kiểm tra tương quan. Giá trị mặc định là "0px".

Hook sử dụng `useEffect` để thiết lập `IntersectionObserver` khi component được tạo ra và loại bỏ `observer` khi component bị hủy. Nó cũng cập nhật `observer` khi `ref` hoặc `rootMargin` thay đổi. Hook trả về một giá trị boolean `isVisible` cho biết liệu phần tử DOM có hiển thị hiện tại hay không.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useOnScreen"
import { useRef } from 'react';
import useOnScreen from './useOnScreen';

export default function OnScreenComponentComponent() {
  const headerTwoRef = useRef();
  const visible = useOnScreen(headerTwoRef, '-100px');

  return (
    <div>
      <h1>Header</h1>
      <div>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Unde incidunt, nam id itaque error dicta? Numquam
        earum iusto optio officia, molestias debitis illum facilis nemo asperiores eaque voluptates modi? Dicta mollitia
        fugit doloremque vitae, dolores sequi fuga quas vel incidunt animi architecto dignissimos amet in quam
        praesentium corrupti voluptate dolorem impedit numquam aut cupiditate nulla! Nisi dolore dicta, cumque illum
        tempora enim dolores eum quis itaque nostrum architecto vel cum officiis aperiam qui exercitationem
        voluptatibus. Veritatis unde doloribus dolorem architecto, eum reprehenderit possimus similique eius cum
        obcaecati totam placeat. Delectus nulla, quae temporibus omnis assumenda autem ad quibusdam facilis aspernatur
        inventore nobis.
      </div>
      <h1 ref={headerTwoRef}>Header 2 {visible && '(Visible)'}</h1>
      <div>
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Unde incidunt, nam id itaque error dicta? Numquam
        earum iusto optio officia, molestias debitis illum facilis nemo asperiores eaque voluptates modi? Dicta mollitia
        fugit doloremque vitae, dolores sequi fuga quas vel incidunt animi architecto dignissimos amet in quam
        praesentium corrupti voluptate dolorem impedit numquam aut cupiditate nulla! Nisi dolore dicta, cumque illum
        tempora enim dolores eum quis itaque nostrum architecto vel cum officiis aperiam qui exercitationem
        voluptatibus. Veritatis unde doloribus dolorem architecto, eum reprehenderit possimus similique eius cum
        obcaecati totam placeat. Delectus nulla, quae temporibus omnis assumenda autem ad quibusdam facilis aspernatur
        inventore nobis. lorem ad, doloribus hic! Qui corporis perspiciatis dolores rem minima tenetur. Fugit ipsa
        consectetur ad reiciendis, quia iste, sapiente rerum exercitationem reprehenderit laborum eligendi cumque? Quia
        porro modi repudiandae nostrum accusamus! adipisci!
      </div>
    </div>
  );
}

Hook useOnScreen có thể hữu ích khi bạn muốn theo dõi khi một phần tử cụ thể trong DOM xuất hiện hoặc biến mất khỏi tầm nhìn, ví dụ như để tải ảnh một cách lười biếng, theo dõi vị trí cuộn, hoặc hiển thị các phần tử theo nhu cầu.

15. useWindowSize

```jsx title="useWindowSize.jsx" import { useState } from 'react'; import useEventListener from '../13-useEventListener/useEventListener';

export default function useWindowSize() { const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight, });

useEventListener('resize', () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight }); });

return windowSize; }


`useWindowSize` là một hook React tùy chỉnh cho phép một component theo dõi kích thước hiện tại của cửa sổ trình duyệt. Nó sử dụng hook `useState` tích hợp sẵn từ thư viện React và một hook tùy chỉnh gọi là `useEventListener`, giúp một component thêm một trình nghe sự kiện vào một phần tử DOM cụ thể và thực thi một hàm gọi lại khi sự kiện xảy ra.

Hook tạo ra một đối tượng gọi là `windowSize` chứa chiều rộng và chiều cao của cửa sổ trình duyệt và thiết lập trạng thái ban đầu bằng cách sử dụng các thuộc tính `window.innerWidth``window.innerHeight`.

Nó sử dụng hook `useEventListener` để thêm một trình nghe sự kiện `resize` vào đối tượng cửa sổ và cập nhật trạng thái với chiều rộng và chiều cao mới của cửa sổ khi sự kiện xảy ra. Nó trả về đối tượng `windowSize`, chứa chiều rộng và chiều cao hiện tại của cửa sổ trình duyệt.

Dưới đây là một ví dụ về cách sử dụng hook này:

```jsx title="Ví dụ với useWindowSize"
import useWindowSize from './useWindowSize';

export default function WindowSizeComponent() {
  const { width, height } = useWindowSize();

  return (
    <div>
      {width} x {height}
    </div>
  );
}

Hook này có thể hữu ích trong những tình huống bạn muốn tạo thiết kế đáp ứng và điều chỉnh bố cục hoặc hành vi của một component dựa trên kích thước của cửa sổ trình duyệt.

II. Kết luận

Những hook tùy chỉnh này có thể là rất quan trọng khi xây dựng các dự án có thể mở rộng và không lỗi. Chúng hoạt động tốt, ngay cả trên các dự án lớn. Do đó, hãy thoải mái sử dụng chúng bất cứ khi nào bạn cần.

III. Tài liệu tham khảo