---
title: Skróty klawiszowe
image: /images/user-guide/table-views/table.png
---

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

## Wprowadzenie

Gdy potrzebujesz nasłuchiwać skrótu klawiszowego, zazwyczaj używasz nasłuchiwania zdarzenia `onKeyDown`.

Jednak w `twenty-front` mogą wystąpić konflikty między tymi samymi skrótami używanymi w różnych komponentach, zamontowanymi w tym samym czasie.

Na przykład, jeśli masz stronę nasłuchującą klawisza Enter i modal nasłuchujący klawisza Enter z komponentem Select w środku, który również nasłuchuje klawisza Enter, możesz mieć konflikt, gdy wszystkie są zamontowane jednocześnie.

## The `useScopedHotkeys` hook

Aby rozwiązać ten problem, mamy własny hak, który umożliwia nasłuchiwanie skrótów klawiszowych bez konfliktu.

Umieszczasz go w komponencie, a będzie nasłuchiwać skrótów klawiszowych tylko wtedy, gdy komponent jest zamontowany I gdy określony **zakres skrótu klawiszowego** jest aktywny.

## Jak praktycznie nasłuchiwać skrótów klawiszowych?

Ustawianie nasłuchiwania skrótów klawiszowych wymaga dwóch kroków:

1. Określ [zakres skrótu klawiszowego](#what-is-a-hotkey-scope-), który będzie nasłuchiwać skrótów klawiszowych
2. Use the `useScopedHotkeys` hook to listen to hotkeys

Konfiguracja zakresów skrótów klawiszowych jest wymagana nawet na prostych stronach, ponieważ inne elementy interfejsu, jak menu po lewej czy menu komend, mogą także nasłuchiwać skrótów klawiszowych.

## Przykłady użycia skrótów klawiszowych

Ogólnie rzecz biorąc, występują dwa przypadki użycia wymagające skrótów klawiszowych:

1. Na stronie lub w komponencie zamontowanym na stronie
2. W komponencie typu modalnego, który przejmuje fokus ze względu na działanie użytkownika

Drugi przypadek użycia może wystąpić rekurencyjnie: na przykład w rozwijanym menu w modalu.

### Nasłuchiwanie skrótów klawiszowych na stronie

Przykład:

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

### Nasłuchiwanie skrótów klawiszowych w komponencie typu modalnego

W tym przykładzie użyjemy komponentu modalnego, który nasłuchuje klawisza Escape, aby poinformować nadrzędny komponent o konieczności jego zamknięcia.

Tutaj działanie użytkownika zmienia zakres.

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

Następnie w komponencie modalnym:

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

Ważne jest, aby używać tego wzorca, gdy nie jesteś pewny, że samo użycie useEffect z mount/unmount wystarczy, aby uniknąć konfliktów.

Te konflikty mogą być trudne do debugowania i mogą występować częściej niż się wydaje przy użyciu useEffects.

## Co to jest zakres skrótu klawiszowego?

Zakres skrótu klawiszowego to ciąg znaków, który reprezentuje kontekst, w którym skróty klawiszowe są aktywne. Zazwyczaj jest kodowany jako enum.

Gdy zmienisz zakres skrótu klawiszowego, skróty nasłuchujące tego zakresu zostaną włączone, a te, które nasłuchują innych zakresów, zostaną wyłączone.

Możesz ustawić tylko jeden zakres naraz.

As an example, the hotkey scopes for each page are defined in the `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',
}
```

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

Ale ten stan Recoil nigdy nie powinien być obsługiwany ręcznie! Pokażemy, jak go używać w następnej sekcji.

## Jak to działa wewnętrznie?

Stworzyliśmy cienką warstwę na [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro), która sprawia, że jest bardziej wydajna i unika niepotrzebnych ponownych renderowań.

Tworzymy również stan Recoil, aby obsługiwać stan zakresu skrótu klawiszowego i udostępniać go wszędzie w aplikacji.