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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-use-popup

v1.0.3

Published

Popups and modal management for react-fiber SPA apps based on custom events (without contexts)

Downloads

3

Readme

Popups management for React SPA

You can read this readme in English.

Motivation / Features

  • [x] Компоненты в попапе имеют окружение доменной области
  • [x] Не хочется иметь централизованное хранилище попапов
  • [x] Можно открывать попапы из любого места приложения
  • [x] Простая API
  • [x] Можно использовать вне реакта (например, в STM)
  • [ ] Поддержка microfrontends

Этот пакет не реализует UI модальных окон. Он предназначен только для управления ими в приложении

Вы можете использовать его с любыми UI-попапами в React, например, с модальными окнами из Material-UI, Ant Design, react-modal или любыми другими

Little bit of theory / Vocabulary

Попап - UI-компонент с контентом, который может быть показан либо скрыт в зависимости от значения некой props-переменной.

Сами попапы не содержат в себе бизнес-логику приложения.

Попапы можно разделить на динамические и статическиме, а также на локальные и глобальные.

Динамический попап - Содержимое такого попапа маунтится и анмаунтится только при открытии и закрытии попапа.

Статический попап - Содержимое такого попапа маунтится и анмаунтится вместе с доменной областью в которой будет использоваться.

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

Как правило, такие попапы сохраняют состояния между открытиями, что может быть полезно в некоторых задачах

Экшены - компоненты которые являются контейнерами для попапов.

В самом простом случае они содержат бизнес-логику приложения, нужны только для открытия и закрытия попапов.

пример:

import type { FC, PropsWithChildren } from "react";
import { usePopup } from "react-use-popup";

const ExampleAction: FC<PropsWithChildren> = props => {
  const { children } = props;
  const visible = usePopup("popup-example");

  return (
    <Popup visible={visible}>
      {children}
    </Popup>
  );
};

Вы можете располагать экшены в любом месте приложения, где вам удобно.

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

Далеко не все попапы (скорее практически никакие) не должны быть прям совсем глобальными.

Локальные попапы - попапы, которые открываются только в одном конкретном месте приложения. Экшн с таким попапом удобно располагать прямо в компоненте, где он будет использоваться.

<>
  <Button onClick={() => openPopup("popup-example")}>Open popup</Button>
  <ExampleAction>local popup content</ExampleAction>
</>

Глобальные попапы - попапы, которые могут быть открыты из любого места конкретной доменной области приложения. Экшн с таким попапом удобно располагать в корневом компоненте доменной области.

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

<>
  <...>
      <Button onClick={() => openPopup("popup-example")}>Open popup</Button>
  </...>

  <...>
      <Button onClick={() => openPopup("popup-example")}>Open popup</Button>
  </...>

  <ExampleAction>global popup content</ExampleAction>
</>

При этом если пользователь покинет доменную область, попап будет размонтирован.

Usage

Для каждого экшена нужно завести уникальный intent: srting - ключ для открытия попапа с этим экшеном

import type { FC, PropsWithChildren } from "react";
import { usePopup } from "react-use-popup";

// Удобно описывать intent в компоненте экшена и экспортировать из него
export const intent = "popup-example";

const ExampleAction: FC<PropsWithChildren> = props => {
  const { children } = props;

  // использование intent для получения состояния открытия попапа
  const visible = usePopup(intent);

  return (
    <Popup visible={visible}>
      {children}
    </Popup>
  );
};

Для открытия попапа надо просто вызвать метод открытия с нужным intent.

import { openPopup } from "react-use-popup";
import { intent } from "./ExampleAction";
...
<Button onClick={() => openPopup(intent)}>Open popup</Button>

Это работает и в реакте и за его пределами (redux, эффектор, саги и т.д.)

Почему? Для управления поапами используется CustomEvent. Контекстом выступает window, который доступен везде.

Для закрытия попапа нужно вызвать метод закрытия с тем же intent.

import { closePopup } from "react-use-popup";
import { intent } from "./ExampleAction";
...
<Button onClick={() => closePopup(intent)}>Close popup</Button>

Вы также можете установить обработчики, которые будут вызваны при открытии / закрытии попапа

Handbook

Передача параметров в компонент в попапе

Просто передайте их как пропсы. Или используйте контексты, хуки и т.д. из вашей доменной области

Вы можете использовать useEffect на пропсы как обычно

import type { UUID } from "node:crypto";
import type { FC } from "react";
import { useParams } from "react-router";
import { usePopup } from "react-use-popup";

export const intent = "popup-example";

const ExampleAction: FC = props => {
  const { articleId } = useParams() as { articleId: UUID };
  const visible = usePopup(intent);

  return (
    <Popup visible={visible}>
      <Article id={articleId} />
    </Popup>
  );
};

В динамических попапах удобно использовать useEffect на маунт

const Article: FC<{ id: UUID }> = props => {
  const { id } = props;

  useEffect(() => {
    fetchArticle(id);
  }, []);

  return <div>Article content</div>;
};

Передача параметров при открытии

Это актуально для статических попапов. В динамических попапах вероятно проще использовать useEffect на маунт (см. выше)

Вы можете передать объект с параметрами в метод открытия попапа

import { openPopup } from "react-use-popup";
import { intent } from "./ExampleAction";
...
const openHandler = useCallback(
  () => openPopup(intent, { userId }),
  [userId]
);
...
<Button onClick={openHandler}>Open popup</Button>

Эти параметры будут переданы в перехватчик открытия и вы сможете обработать их

const ExampleAction: FC = () => {
  const visible = usePopup(intent, {
    open: ({ userId }) => sendAnalytics("popup opened", intent, userId)
  });

  return (
    <Popup visible={visible}>
      ...
    </Popup>
  );
};

В этом кейсе не рекомендуется менять пропсы компонента в попапе.

Лучше вызвать метод из компонента напрямую (см. ниже)

Загрузка данных при открытии попапа

Идея в том чтобы логика компонента внутри попапа не знала о том, что он находится в попапе.

Однако, если мы не можем использовать useEffect на маунт (например в статических попапах), то можно передать управление наружу (лучше всего с помощью ref / useImperativeHandle)

const ExampleAction: FC = () => {
  const ref = useRef(null);

  const visible = usePopup(intent, {
    open: ({ userId }) => ref.current?.loadData(userId)
  });

  return (
    <Popup visible={visible}>
      <PopupContent ref={ref} />
    </Popup>
  );
};

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

const PopupContent = props => {
  const { ref } = props;
  const [data, setData] = useState(null);

  const loadData = useCallback(async (userId: UUID) => {
    const data = await fetchData(userId);
    setData(data);
  }, []);

  useImperativeHandle(ref, () => ({ loadData }), [loadData]);

  return <div>{data}</div>;
};

Отправка формы из попапа перед закрытием

  • Форма в попапе сама управляет логикой отправки данных сервер, а чтобы попап закрылся после успешной отправки, нужно вызвать метод закрытия попапа. Для этого передадим его в форму
  • А из формы экспортируем контроллер отправки и повесим его на кнопку в попапе
import { closePopup, usePopup } from "react-use-popup";

export const intent = "popup-example";

const closeHandler = () => closePopup(intent);

const ExampleAction: FC = () => {
  const ref = useRef(null);
  const visible = usePopup(intent);

  return (
    <Popup visible={visible}>
      <PopupContent closePopup={closeHandler} ref={ref} />

      <SubmitButton onClick={() => ref.current?.sendForm()} />
    </Popup>
  );
};

Внутри компонента, который будет в попапе мы описываем логику отправки, так как находимся непосредственно в бизнес-логике приложения.

const PopupContent = props => {
  const { closeHandler, ref } = props;

  const sendForm = useCallback(async (userId: UUID) => {
    try {
      await sendFormData(userId);
      closeHandler();
    } catch (error) {
      console.error(error);
    }
  }, [closeHandler]);

  useImperativeHandle(ref, () => ({ sendForm }), [sendForm]);

  return <form>...</form>;
};

Тут форма сама управляет логикой отправки себя на сервер

  • если ошибка - показываем ошибку
  • если успех - тогда после отправки закрываем попап

Открытие второго попапа для подтверждения

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

теперь просто открываем новый экшн поверх старого и передаем ему обработчик confirm формы

Работа с роутером - реакция на изменение url

const { pathname } = useLocation();

useEffect(() => {
  if (!pathname.endsWith("/popup")) return;

  openPopup(intent);
 }, [pathname]);

Изменение урла при открытии попапа не имеет смысла - лучше просто изменить урл + использовать код выше → поведение будет тоже самое

Бонус - мультиинстансинг

это когда попап один и тот же, при этом открыто несколько окон одновременно, а содержимое разное

Тут можно разрулить на уровне Action

Единственное, так как история кастомная, нужно будет не использовать хук usePopup, а самостоятельно сделать обработчики - они должны создавать инстанс попапа и добавлять в список, который будет рендериться в этом Action

issue: Поддержка мультиинстансинга

Installation

$ npm install react-use-popup

API / Types

openPopup

openPopup<OpenParams>(intent: string, detail?: OpenParams): void

открывает попап с указанным intent и передает параметры в обработчик открытия

closePopup

closePopup<CloseParams>(intent: string, detail?: CloseParams): void

закрывает попап с указанным intent и передает параметры в обработчик закрытия

usePopup [react-hook]

usePopup(intent: string, hooks?: UsePopupHooks<OpenParams, CloseParams>): boolean

возвращает состояние открытия попапа с указанным intent и позволяет установить обработчики открытия и закрытия

type UsePopupHooks<OpenParams, CloseParams> = {
  open?: (detail: OpenParams) => void;
  close?: (detail: CloseParams) => void;
}

ROADMAP