3.4.5 • Published 6 months ago

@ws-serenity/react-hooks v3.4.5

Weekly downloads
-
License
ISC
Repository
gitlab
Last release
6 months ago

Базовые хуки

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 },
});
ПараметрТипПо умолчаниюОписание
keyPressParamsUseKeyPressParamsОбъект, описывающий обработчики для каждого ключа.
targetRefObject<HTMLElement>Элемент, на котором необходимо отловить событие.
optionsAddEventListenerOptions{ 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Обработчик для выбранных файлов.
multiplebooleanfalseРазрешить выбирать несколько файлов.
acceptAcceptConfigЗадает параметры для фильтрации файлов по их расширению и их 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>
ВозвратТипОписание
isActivebooleantrue если перетаскиваемый объект находится в зоне контейнера, false иначе.
isWindowsActivebooleantrue, если перетаскиваемый объект находится в зоне окна, false иначе.
listenersListenersКоллекция listeners на которые нужно подписать контейнер для корректной работы хука.
selectFiles() => voidВызывает browser api для выбора файла, игнорирует dropOnly флаг.
ПараметрТипПо умолчаниюОписание
handleDrop(files: File[]) => voidОбработчик для выбранных файлов.
dropOnlybooleanfalseЕсли true, то файлы буду добавляться только перетаскиванием.
filterDropbooleantrueЕсли false, то файлы, добавленные перетаскиванием, не будут проверяться на соответствие условиям accept.
disabledbooleanfalseЕсли true, то работа хука приостанавливается.
multiplebooleantrueРазрешить выбирать несколько файлов.
acceptAcceptConfigЗадает параметры для фильтрации файлов по их расширению и их 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

3.4.5

6 months ago

3.3.1

10 months ago

3.3.0

10 months ago

3.4.4

6 months ago

3.4.3

7 months ago

3.4.2

9 months ago

3.3.3

9 months ago

3.3.2

9 months ago

3.2.1-preview.1

12 months ago

3.2.1-preview.2

11 months ago

3.2.0-preview.2

12 months ago

2.1.2-preview.2

1 year ago

3.1.2

1 year ago

3.1.1

1 year ago

3.1.0

1 year ago

3.0.0

1 year ago

2.1.2-preview.1

1 year ago

2.1.1

1 year ago

3.2.0-preview.1

1 year ago

2.1.0

1 year ago

2.0.0

1 year ago

1.2.1

1 year ago

1.2.0

1 year ago

1.1.1

1 year ago

1.0.2

1 year ago

0.0.1

1 year ago

1.0.0

2 years ago