npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ws-serenity/react-hooks

v3.4.5

Published

Some common hooks for React application

Downloads

168

Readme

Базовые хуки

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