Интеграция в React

Библиотека Noty не имеет официальной React-обёртки, поэтому интеграция выполняется напрямую через импорт и управление экземплярами уведомлений.

Установка через npm:

npm install noty

Подключение стилей (обязательно, иначе уведомления не будут отображаться корректно):

import 'noty/lib/noty.css';
import 'noty/lib/themes/mint.css';

Подключение самой библиотеки:

import Noty from 'noty';

Базовое использование внутри React-компонента

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

const showNotification = () => {
  new Noty({
    text: 'Операция выполнена',
    type: 'success',
    timeout: 3000
  }).show();
};

Пример компонента:

import React from 'react';
import Noty from 'noty';
import 'noty/lib/noty.css';
import 'noty/lib/themes/mint.css';

function App() {
  const handleClick = () => {
    new Noty({
      text: 'Кнопка нажата',
      type: 'info',
      timeout: 2000
    }).show();
  };

  return <button onCl ick={handleClick}>Показать уведомление</button>;
}

export default App;

Создание собственного хука useNoty

Для повторного использования логики удобно создать кастомный React-хук.

import Noty from 'noty';

export const useNoty = () => {
  const show = ({ text, type = 'alert', timeout = 3000 }) => {
    return new Noty({
      text,
      type,
      timeout
    }).show();
  };

  return { show };
};

Использование:

import React from 'react';
import { useNoty } from './useNoty';

function Example() {
  const { show } = useNoty();

  return (
    <button
      onCl ick={() =>
        show({ text: 'Уведомление из хука', type: 'success' })
      }
    >
      Показать
    </button>
  );
}

Централизованное управление уведомлениями

В крупных приложениях уведомления удобно выносить в отдельный сервис.

// notyService.js
import Noty from 'noty';

class NotyService {
  success(text) {
    this.show(text, 'success');
  }

  error(text) {
    this.show(text, 'error');
  }

  info(text) {
    this.show(text, 'info');
  }

  show(text, type) {
    new Noty({
      text,
      type,
      timeout: 3000
    }).show();
  }
}

export default new NotyService();

Использование:

import NotyService from './notyService';

NotyService.success('Данные сохранены');
NotyService.error('Ошибка загрузки');

Интеграция с Redux

Уведомления можно вызывать в middleware или в thunk/async actions.

Пример с Redux Toolkit:

import { createAsyncThunk } from '@reduxjs/toolkit';
import NotyService from './notyService';

export const fetchData = createAsyncThunk(
  'data/fetch',
  async (_, { rejectWithValue }) => {
    try {
      const response = await fetch('/api/data');
      const data = await response.json();

      NotyService.success('Данные загружены');

      return data;
    } catch (error) {
      NotyService.error('Ошибка запроса');
      return rejectWithValue(error);
    }
  }
);

Использование в useEffect

Для отображения уведомлений при изменении состояния:

import { useEffect } from 'react';
import Noty from 'noty';

function StatusComponent({ status }) {
  useEffect(() => {
    if (status === 'success') {
      new Noty({
        text: 'Успешно выполнено',
        type: 'success'
      }).show();
    }
  }, [status]);

  return null;
}

Контроль жизненного цикла уведомлений

Noty возвращает экземпляр, которым можно управлять:

const notification = new Noty({
  text: 'Загрузка...',
  timeout: false
}).show();

// Закрытие вручную
notification.close();

В React это полезно при работе с асинхронными операциями:

const handleAsync = async () => {
  const loader = new Noty({
    text: 'Загрузка...',
    timeout: false
  }).show();

  try {
    await fetch('/api');

    loader.close();

    new Noty({
      text: 'Готово',
      type: 'success'
    }).show();
  } catch {
    loader.close();

    new Noty({
      text: 'Ошибка',
      type: 'error'
    }).show();
  }
};

Кастомизация через props-подобный подход

Можно создать компонент-обёртку, имитирующий декларативный стиль React:

import { useEffect } from 'react';
import Noty from 'noty';

function NotyMessage({ text, type, trigger }) {
  useEffect(() => {
    if (trigger) {
      new Noty({ text, type }).show();
    }
  }, [trigger]);

  return null;
}

Использование:

<NotyMessage text="Сохранено" type="success" trigger={isSaved} />

Работа с очередями уведомлений

Noty поддерживает очередь (queue), что удобно при множественных уведомлениях:

new Noty({
  text: 'Первое сообщение',
  queue: 'main'
}).show();

new Noty({
  text: 'Второе сообщение',
  queue: 'main'
}).show();

Настройка глобального поведения очереди:

Noty.setMaxVisible(3, 'main');

Темизация и стилизация

Подключение разных тем:

import 'noty/lib/themes/relax.css';

Или использование кастомных классов:

new Noty({
  text: 'Кастомный стиль',
  theme: 'mint',
  className: 'my-custom-class'
}).show();

CSS:

.my-custom-class {
  background: #222;
  color: #fff;
}

Интеграция с TypeScript

Типизация базового использования:

import Noty from 'noty';

type NotyType = 'alert' | 'success' | 'error' | 'warning' | 'info';

interface NotyOptions {
  text: string;
  type?: NotyType;
  timeout?: number | boolean;
}

const showNoty = (options: NotyOptions) => {
  new Noty(options).show();
};

Особенности работы в React

Императивная модель Noty В отличие от типичных React-библиотек, Noty не использует JSX и виртуальный DOM. Это означает:

  • уведомления создаются вне React-дерева
  • нет автоматической синхронизации состояния
  • управление происходит вручную

Потенциальные проблемы:

  • утечки уведомлений при частых рендерах
  • дублирование вызовов в Strict Mode
  • сложность тестирования

Оптимизация и best practices

1. Изоляция логики уведомлений Всегда выносить в отдельный слой (хук или сервис)

2. Избегать вызовов внутри render Только в обработчиках или useEffect

3. Контроль повторных вызовов Особенно в Strict Mode React 18

const calledRef = useRef(false);

useEffect(() => {
  if (!calledRef.current) {
    new Noty({ text: 'Один раз' }).show();
    calledRef.current = true;
  }
}, []);

4. Очистка при размонтировании

useEffect(() => {
  const n = new Noty({ text: 'Сообщение' }).show();

  return () => {
    n.close();
  };
}, []);

Интеграция с формами

Пример с обработкой ошибок:

const handleSubmit = async () => {
  try {
    await submitForm();
    NotyService.success('Форма отправлена');
  } catch {
    NotyService.error('Ошибка формы');
  }
};

Интеграция с API-слоем

Общий interceptor (например, для fetch):

export const apiRequest = async (url) => {
  try {
    const res = await fetch(url);

    if (!res.ok) {
      throw new Error();
    }

    return await res.json();
  } catch (e) {
    NotyService.error('Ошибка API');
    throw e;
  }
};

Ограничения и альтернативные подходы

  • отсутствие декларативности
  • нет встроенной интеграции с React Context
  • ручное управление состоянием уведомлений

При необходимости более тесной интеграции с React часто создаётся собственный слой абстракции поверх Noty, превращающий его в управляемую систему уведомлений через Context API или глобальное состояние.