---
title: Гарячі клавіші
image: /images/user-guide/table-views/table.png
---

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

## Вступ

Коли вам потрібно слухати гарячу клавішу, зазвичай ви використовуєте `onKeyDown` слухач подій.

Однак у `twenty-front` можуть виникнути конфлікти між однаковими гарячими клавішами, які використовуються в різних компонентах, встановлених одночасно.

Наприклад, якщо у вас є сторінка, яка слухає клавішу Enter, модальне вікно, що слухає клавішу Enter, і компонент Select всередині цього модального вікна, який слухає клавішу Enter, може виникнути конфлікт, коли всі вони встановлені одночасно.

## Гачок `useScopedHotkeys`

Для вирішення цієї проблеми ми розробили користувальницький гачок, який дозволяє слухати гарячі клавіші без будь-яких конфліктів.

Ви розміщуєте його в компоненті, і він буде слухати гарячі клавіші лише тоді, коли компонент встановлений І коли вказаний **скоуп гарячих клавіш** активний.

## Як слухати гарячі клавіші на практиці?

Є два кроки, необхідні для налаштування слухання гарячих клавіш:

1. Встановіть [скоуп гарячих клавіш](#what-is-a-hotkey-scope-), який буде слухати гарячі клавіші
2. Use the `useScopedHotkeys` hook to listen to hotkeys

Налаштування скоупів гарячих клавіш потрібне навіть на простих сторінках, адже інші елементи інтерфейсу, такі як ліве меню чи команда меню, також можуть слухати гарячі клавіші.

## Випадки використання гарячих клавіш

Загалом, у вас буде два випадки використання гарячих клавіш:

1. На сторінці або компоненті, встановленому на сторінці
2. У модальному компоненті, який бере на себе фокус через дію користувача

Другий випадок використання може відбуватися рекурсивно: наприклад, випадаюче меню в модальному вікні.

### Слухання гарячих клавіш на сторінці

Приклад :

```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>;
};
```

### Слухання гарячих клавіш в компоненті типу модального вікна

У цьому прикладі ми використовуватимемо модальний компонент, який слухає гарячу клавішу Escape, щоб повідомити його батька про закриття.

Тут взаємодія користувача змінює скоуп.

```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>;
};
```

Потім у модальному компоненті:

```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>;
};
```

Важливо скористатися цим шаблоном, коли ви не впевнені, що лише використання useEffect з mount/unmount буде достатньо, щоб уникнути конфліктів.

Ці конфлікти можуть бути важко виправити, і це може відбуватися частіше, ніж ви думаєте, з використанням useEffect.

## Що таке скоуп гарячих клавіш?

Скоуп гарячих клавіш - це рядок, що представляє контекст, в якому активні гарячі клавіші. Зазвичай він закодований у вигляді enum.

Коли ви змінюєте скоуп гарячих клавіш, гарячі клавіші, що слухають цей скоуп, будуть увімкнені, а гарячі клавіші, що слухають інші скоупи, будуть вимкнені.

Одночасно ви можете встановити лише один скоуп.

Як приклад, скоупи гарячих клавіш для кожної сторінки визначені в `PageHotkeyScope` enum:

```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',
}
```

Внутрішньо обраний скоуп зберігається в стані Recoil, який розділений по всій програмі:

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

Але цей стан Recoil не можна керувати вручну! Ми розглянемо, як ним користуватися в наступному розділі.

## Як це працює внутрішньо?

Ми створили тонку оболонку поверх [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro), яка робить його більш продуктивним і запобігає зайвим перерендерінгам.

Ми також створюємо стан Recoil для обробки стану скоупа гарячих клавіш і забезпечуємо його доступність по всій програмі.