---
title: Phím nóng
image: /images/user-guide/table-views/table.png
---

<Frame>
  <img src="/images/user-guide/table-views/table.png" alt="Header" />
</Frame>

## Giới thiệu

When you need to listen to a hotkey, you would normally use the `onKeyDown` event listener.

Trong `twenty-front`, bạn có thể gặp xung đột giữa các phím nóng giống nhau được sử dụng trong các thành phần khác nhau, gắn kết cùng một lúc.

Ví dụ, nếu bạn có một trang lắng nghe phím Enter, và một modal lắng nghe phím Enter, với thành phần Select bên trong modal đó cũng lắng nghe phím Enter, bạn có thể gặp xung đột khi tất cả được gắn kết cùng một lúc.

## Hook `useScopedHotkeys`

Để xử lý vấn đề này, chúng tôi có một hook tuỳ chỉnh giúp lắng nghe phím nóng mà không gặp xung đột nào.

You place it in a component, and it will listen to the hotkeys only when the component is mounted AND when the specified **hotkey scope** is active.

## Làm thế nào để thực hành lắng nghe phím nóng?

Có hai bước liên quan trong việc thiết lập lắng nghe phím nóng:

1. Thiết lập [khoảng phím nóng](#what-is-a-hotkey-scope-) sẽ lắng nghe các phím nóng
2. Sử dụng hook `useScopedHotkeys` để lắng nghe phím nóng

Thiết lập khoảng phím nóng là cần thiết ngay cả trong các trang đơn giản, bởi vì các thành phần giao diện người dùng khác như menu bên trái hoặc menu lệnh cũng có thể lắng nghe phím nóng.

## Các trường hợp sử dụng phím nóng

Nói chung, bạn sẽ có hai trường hợp sử dụng cần phím nóng:

1. Trong một trang hoặc một thành phần được gắn kết trong một trang
2. Trong một thành phần kiểu modal mà lấy tiêu điểm do hành động của người dùng

Trường hợp thứ hai có thể xảy ra đệ quy: ví dụ một hộp xuống (dropdown) trong một modal.

### Lắng nghe phím nóng trong một trang

Ví dụ :

```tsx
const PageListeningEnter = () => {
  const {
    setHotkeyScopeAndMemorizePreviousScope,
    goBackToPreviousHotkeyScope,
  } = usePreviousHotkeyScope();

  // 1. Set the hotkey scope in a useEffect
  useEffect(() => {
    setHotkeyScopeAndMemorizePreviousScope(
      ExampleHotkeyScopes.ExampleEnterPage,
    );

    // Revert to the previous hotkey scope when the component is unmounted
    return () => {
      goBackToPreviousHotkeyScope();
    };
  }, [goBackToPreviousHotkeyScope, setHotkeyScopeAndMemorizePreviousScope]);

  // 2. Use the useScopedHotkeys hook
  useScopedHotkeys(
    Key.Enter,
    () => {
      // Some logic executed on this page when the user presses Enter
      // ...
    },
    ExampleHotkeyScopes.ExampleEnterPage,
  );

  return <div>My page that listens for Enter</div>;
};
```

### Lắng nghe phím nóng trong một thành phần kiểu modal

Trong ví dụ này, chúng ta sẽ sử dụng một thành phần modal để lắng nghe phím Escape để báo cho thành phần cha đóng nó lại.

Here the user interaction is changing the scope.

```tsx
const ExamplePageWithModal = () => {
  const [showModal, setShowModal] = useState(false);

  const {
    setHotkeyScopeAndMemorizePreviousScope,
    goBackToPreviousHotkeyScope,
  } = usePreviousHotkeyScope();

  const handleOpenModalClick = () => {
    // 1. Set the hotkey scope when user opens the modal
    setShowModal(true);
    setHotkeyScopeAndMemorizePreviousScope(
      ExampleHotkeyScopes.ExampleModal,
    );
  };

  const handleModalClose = () => {
    // 1. Revert to the previous hotkey scope when the modal is closed
    setShowModal(false);
    goBackToPreviousHotkeyScope();
  };

  return <div>
    <h1>My page with a modal</h1>
    <button onClick={handleOpenModalClick}>Open modal</button>
    {showModal && <MyModalComponent onClose={handleModalClose} />}
  </div>;
};
```

Sau đó trong thành phần modal:

```tsx
const MyDropdownComponent = ({ onClose }: { onClose: () => void }) => {
  // 2. Use the useScopedHotkeys hook to listen for Escape.
  // Note that escape is a common hotkey that could be used by many other components
  // So it's important to use a hotkey scope to avoid conflicts
  useScopedHotkeys(
    Key.Escape,
    () => {
      onClose()
    },
    ExampleHotkeyScopes.ExampleModal,
  );

  return <div>My modal component</div>;
};
```

It's important to use this pattern when you're not sure that just using a useEffect with mount/unmount will be enough to avoid conflicts.

Những xung đột này có thể khó gỡ lỗi, và có thể xảy ra thường xuyên hơn bạn nghĩ với useEffects.

## Khoảng phím nóng là gì?

Khoảng phím nóng là một chuỗi biểu thị một bối cảnh mà trong đó các phím nóng đang hoạt động. Nó thường mã hóa như một enum.

Khi bạn thay đổi khoảng phím nóng, các phím nóng lắng nghe khoảng này sẽ được kích hoạt và các phím nóng lắng nghe các khoảng khác sẽ bị vô hiệu hoá.

Bạn có thể chỉ thiết lập một phạm vi tại một thời điểm.

Ví dụ, các khoảng phím nóng cho mỗi trang được định nghĩa trong enum `PageHotkeyScope`:

```tsx
export enum PageHotkeyScope {
  Settings = 'settings',
  CreateWorkspace = 'create-workspace',
  SignInUp = 'sign-in-up',
  CreateProfile = 'create-profile',
  PlanRequired = 'plan-required',
  ShowPage = 'show-page',
  PersonShowPage = 'person-show-page',
  CompanyShowPage = 'company-show-page',
  CompaniesPage = 'companies-page',
  PeoplePage = 'people-page',
  OpportunitiesPage = 'opportunities-page',
  ProfilePage = 'profile-page',
  WorkspaceMemberPage = 'workspace-member-page',
  TaskPage = 'task-page',
}
```

Internally, the currently selected scope is stored in a Recoil state that is shared across the application :

```tsx
export const currentHotkeyScopeState = createState<HotkeyScope>({
  key: 'currentHotkeyScopeState',
  defaultValue: INITIAL_HOTKEYS_SCOPE,
});
```

Nhưng trạng thái Recoil này không bao giờ nên được xử lý thủ công! Chúng ta sẽ xem cách sử dụng nó trong phần tiếp theo.

## Nó hoạt động như thế nào bên trong?

Chúng tôi đã tạo một lớp bao mỏng trên `react-hotkeys-hook` làm cho nó hiệu suất cao hơn và tránh việc tái render không cần thiết.

Chúng tôi cũng tạo trạng thái Recoil để xử lý trạng thái phạm vi phím nóng và làm cho nó có sẵn ở mọi nơi trong ứng dụng.