@ws-serenity/react-hooks
TypeScript icon, indicating that this package has built-in type declarations

3.4.5 • Public • Published

Базовые хуки

Repository

useDebounce

Функция откладывает вызов колбэка на указанное время

const [ searchText, setSearchText ] = useState('');
const [ displayedParticipants, setDisplayedParticipants ] = useState(userParticipants);

// будет вызвана, когда ввод прекратится на SEARCH_INPUT_DEBOUNCE_DELAY милисекунд
const onSetDisplayedParticipants = (searchText: string) => {
    setDisplayedParticipants(userParticipants.filter(user => user.person.displayName.includeIgnoringCase(searchText)));
};
const debounceFilter = useDebounce<string>(onSetDisplayedParticipants, SEARCH_INPUT_DEBOUNCE_DELAY);
useEffect(() => debounceFilter(searchText), [ searchText ]);

useOutsideClick

Колбэк вызывается, когда происходит нажатие за пределами ref указанного компонента

const [ isMenuOpen, setIsMenuOpen ] = useState(false);
const menuRef = useOutsideClick<HTMLDivElement>(() => setIsMenuOpened(false));

useResizeObserver

Следит за изменением размера любого HTML-элемента, при изменении вызывает колбэк. Возвращает ref, за обновлением элемента которого будет следить.

const updateSizeCount = useCallback(() => {
    // обновление стейта работает, но для этого неоходим дополнительный объект с постоянной ссылкой, чтобы замыкание сработало на него
    sizeChangedCountRef.current = sizeChangedCountRef.current + 1;
    setSizeChangedCount(sizeChangedCountRef.current);
}, []);

// так делать НЕ НАДО
const WRONG_updateSizeCount = useCallback(() => {
    // в противном случае значение стейта - значимого типа (sizeChangedCount) - всегда будет оставаться прежним, потому что замкнется 
    setSizeChangedCount(sizeChangedCount + 1);
}, []);

const containerRef = useResizeObserver<HTMLUListElement>(updateSizeCount);

useTouchHold

Предназначен для вызова колбэка при удержании элемента на мобильном и планшетном устройстве

function SomeComponent() {
    const ref = useTouchHold<HTMLDivElement>(onOpenParticipantsModal, OPEN_PARTICIPANTS_MODAL_DELAY);

    return (
        <div
            className={'perticipant'}
            ref={ref}
        >
        </div>
    )
}

useKeyPress

Позволяет добавлять хэндлеры для нажатия определенных клавиш.

 useKeyPress({
    keyPressParams: {
        Escape: {
            onKeyDown: () => setMessage('There\'s no escape from simulation'),
        },
        ArrowUp: {
            onKeyDown: () => {},
        },
        ArrowLeft: {
            onKeyDown: () => {},
        },
        ArrowDown: {
            onKeyDown: () => {},
        },
        ArrowRight: {
            onKeyDown: () => {},
        },
    },
    options: { passive: true },
});
Параметр Тип По умолчанию Описание
keyPressParams UseKeyPressParams Объект, описывающий обработчики для каждого ключа.
target RefObject<HTMLElement> Элемент, на котором необходимо отловить событие.
options AddEventListenerOptions { capture: true } Опции стандартного EventListner'а.

useFileSelect

Вызывает API браузера для выбора файла.

import { useFileSelect } from '@ws-serenity/react-hooks';

const selectFile = useFileSelect({
    handleSelect: () => { /* ... */ },
    multiple: true,
    accept: {
        extensions: [ 'ftl', 'gtl' ],
    },
});

selectFile();
Параметр Тип По умолчанию Описание
handleSelect (files: File[]) => void Обработчик для выбранных файлов.
multiple boolean false Разрешить выбирать несколько файлов.
accept AcceptConfig Задает параметры для фильтрации файлов по их расширению и их mime type.
export type AcceptConfig = {
    // Регистр не учитывается
    // Должны быть заданы без точки
    // [ '.pdf', '.txt' ] - ⚠️ неправильно
    // [ 'pdf', 'PDF', 'txt', 'TXT' ] - ⚠️ допустимо
    // [ 'pdf', 'txt' ] - ✅ правильно
    extensions?: string[],
    // Регистр не учитывается
    // Будет расширено до обобщения images -> images/*
    // [ 'image/jpeg', 'image/png' ... ] - ⚠️ неправильно
    // [ 'IMAGE', 'VIDEO' ] - ⚠️ допустимо, но с нарушением name convention
    // [ 'image', 'video' ] - ✅ правильно
    mimeTypes?: string[],
}

useDropZone

Пример

Отслеживает перетаскивание файлов в контейнер.
Также, может обработать клик на контейнер, вызвав browser api для выбора файла.
Возвращает флаг isActive, коллекцию listeners и метод selectFiles.

import { useDropZone } from '@ws-serenity/react-hooks';

const {
    isActive,
    listeners,
    selectFiles
} = useDropZone();

/* ... */

<div
    className={isActive ? 'drop-zone drop-zone--active' : 'drop-zone'}
    {...listeners}
>
    {/* ... */}
</div>
<button onClick={selectFiles}>Add files</button>
Возврат Тип Описание
isActive boolean true если перетаскиваемый объект находится в зоне контейнера, false иначе.
isWindowsActive boolean true, если перетаскиваемый объект находится в зоне окна, false иначе.
listeners Listeners Коллекция listeners на которые нужно подписать контейнер для корректной работы хука.
selectFiles () => void Вызывает browser api для выбора файла, игнорирует dropOnly флаг.
Параметр Тип По умолчанию Описание
handleDrop (files: File[]) => void Обработчик для выбранных файлов.
dropOnly boolean false Если true, то файлы буду добавляться только перетаскиванием.
filterDrop boolean true Если false, то файлы, добавленные перетаскиванием, не будут проверяться на соответствие условиям accept.
disabled boolean false Если true, то работа хука приостанавливается.
multiple boolean true Разрешить выбирать несколько файлов.
accept AcceptConfig Задает параметры для фильтрации файлов по их расширению и их mime type.

AcceptConfig - см. useFileSelect.AcceptConfig.

useCallbackRef

Хук, объединяющий cb = useCallback( /* ... */ ) и cbRef = useRef(cb).

import { useCallback } from "react";
import { useCallbackRef } from "@ws-serenity/react-hooks";

const callback = useCallback(() => { /* ... */ }, []);
const [ cb, cbRef ] = useCallbackRef(() => { /* ... */ }, []);

Мотивация - невозможность обновить анонимную функцию, использующую коллбэк, например:

import { useCallback } from "react";

type AirType = 'normal' | 'discharged';
const air: AirType = 'normal';
const breathe = useCallback(() => { /* ... */ }, [ air ]);

const human = {
    /* ... */
    breathe: async () => breathe()
}

В примере выше, метод human.breathe() не обновится вместе с breathe. Чтобы это исправить, можно использовать ссылку на коллбэк.

const [ breathe, breatheRef ] = useCallbackRef(() => { /* ... */}, [ air ]);

const human = { /* ... */ breathe: async () => breatheRef.current() }

Также, этот хук позволяет использовать некоторые оптимизации мемоизации, позволяя не подписываться на изменение коллбэка.

useClipboard

Обработчик для различных событий буфера обмена

import { useClipboard } from '@ws-serenity/react-hooks';

useClipboard({
    onPasteFiles: (files: File[]) => console.log(files),
});

initEsiaAuth

Функция для упрощения авторизации через ESIA, является инициализирующей функцией и ее ЗАПРЕЩАЕТСЯ использовать внутри компонента, потому что функция возвращает 2 хука - один для начала авторизации, второй для ее завершения.

Авторизацию можно легко доработать до "любой сторонней авторизации", например, до GoogleAuth, но пока не требуется

// инициализация хуков в отдельном модуле
import { initEsiaAuth } from '@ws-serenity/react-hooks';

const [
    useAuthEsiaInit,
    useAuthEsiaComplete,
] = initEsiaAuth(
    `${apiUrl}/auth-service/esia/init?redirectUrl=https://${window.location.hostname}/auth/external/esia`,
);

export { useAuthEsiaInit, useAuthEsiaComplete };

// компонент вызова esia, с которым взаимодействует пользователь
export function AuthEsiaButton() {
    // передаем метод авторизации, который необходимо вызвать, мы не можем перенести функцию в init, 
    // чтобы обеспечить совместимость с другими хуками, а не только с глобальными функциями
    const start = useAuthEsiaInit((dto) => signIn('auth-esia', { ...dto, redirect: false }));
    return (
        <button onClick={start}>Войти через ESIA</button>
    );
}

// отдельная страница, на которую мы получим редирект после успешной авторизации
// auth/[code]/esia
export default function EsiaAuthPage({ code }: EsiaAuthPageProps) {
    // сюда можно передать необходимые данные любым способом
    useAuthEsiaComplete(code, [code]);

    return (<AppLoader/>);
} 

useIosFriendlyClick

По какой-то причине просто onclick не срабатывает в некоторых случаях в Web на iOS

С такой проблемой столкнулись в списке опций селекта на ios. Баг воспроизводился во всех браузерах iOS

Советы из интернетов не помогли (https://stackoverflow.com/questions/24077725/mobile-safari-sometimes-does-not-trigger-the-click-event) поэтому было состряпано собственное решение

При использовании хука нет необходимости вручную навешивать onClick

Readme

Keywords

none

Package Sidebar

Install

npm i @ws-serenity/react-hooks

Weekly Downloads

4

Version

3.4.5

License

ISC

Unpacked Size

43.2 kB

Total Files

21

Last publish

Collaborators

  • skukartsev
  • ra.vi.an
  • gransly
  • d.duda
  • blablaprincess
  • a.manakina
  • f.ishchenko