← Назад к вопросам

Для чего нужен EventBus?

2.0 Middle🔥 71 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего нужен EventBus

EventBus — это паттерн для взаимодействия компонентов через события, без прямого соединения между ними. Это инструмент для связи между отдалёнными частями приложения.

Основная идея

Вместо того чтобы компоненты общались друг с другом напрямую, они обмениваются событиями через централизованный EventBus:

Компонент A: "Произошло событие X" -> [EventBus]
                                          |
Компонент B: слушает событие X <- [EventBus]
Компонент C: слушает событие X <- [EventBus]

Пример простого EventBus

class EventBus {
  constructor() {
    this.events = {};
  }
  
  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }
  
  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event]
        .filter(cb => cb !== callback);
    }
  }
  
  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => {
        callback(data);
      });
    }
  }
}

const bus = new EventBus();
bus.emit('userLoggedIn', { userId: 123 });
bus.on('userLoggedIn', (user) => {
  console.log('Пользователь вошёл:', user);
});

Основные проблемы, которые решает EventBus

1. Связанность компонентов (Coupling)

Без EventBus компоненты должны знать друг о друге:

function HeaderComponent() {
  const notificationComponent = new NotificationComponent();
  
  const handleUserLogin = (user) => {
    notificationComponent.show(`Welcome, ${user.name}!`);
  };
  
  return <button onClick={handleUserLogin}>Login</button>;
}

function HeaderComponent() {
  const handleUserLogin = (user) => {
    eventBus.emit('notification', `Welcome, ${user.name}!`);
  };
  
  return <button onClick={handleUserLogin}>Login</button>;
}

function NotificationComponent() {
  useEffect(() => {
    eventBus.on('notification', (message) => {
      showNotification(message);
    });
  }, []);
}

2. Коммуникация на расстоянии

Добраться до компонента глубоко в дереве без передачи props:

function LoginButton() {
  const handleLogin = (user) => {
    eventBus.emit('user:login', user);
    eventBus.emit('notification:show', 'Welcome!');
    eventBus.emit('counter:increment');
  };
}

Когда использовать EventBus

Подходит для:

  • Глобальные события (user logged in, theme changed)
  • Коммуникация между компонентами разных модулей
  • Системные события (network error, app initialized)
  • Notifications, alerts, modals
eventBus.emit('theme:changed', 'dark');
eventBus.emit('user:logout');
eventBus.emit('api:error', { message: 'Server error' });
eventBus.emit('modal:open', { type: 'confirmation' });
eventBus.emit('toast:show', { message: 'Saved!' });

Не подходит для:

  • Локальное состояние компонента (используй useState)
  • Props между родителем и ребёнком (используй props)
  • Состояние приложения (используй Redux/Zustand/Context)

EventBus в React (React Context как альтернатива)

В современном React часто используют Context вместо EventBus:

const UserContext = createContext();

function UserProvider({ children }) {
  const [user, setUser] = useState(null);
  
  const value = {
    user,
    setUser,
    logout: () => setUser(null),
  };
  
  return (
    <UserContext.Provider value={value}>
      {children}
    </UserContext.Provider>
  );
}

function useUser() {
  return useContext(UserContext);
}

function Header() {
  const { user, logout } = useUser();
  return <button onClick={logout}>Logout</button>;
}

EventBus с типизацией (TypeScript)

type EventMap = {
  'user:login': { id: number; name: string };
  'user:logout': void;
  'notification:show': string;
  'theme:change': 'light' | 'dark';
};

class TypedEventBus {
  private events = new Map();
  
  on(event, callback) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event).push(callback);
  }
  
  emit(event, data) {
    const callbacks = this.events.get(event) || [];
    callbacks.forEach(cb => cb(data));
  }
}

const eventBus = new TypedEventBus();
eventBus.emit('user:login', { id: 1, name: 'John' });

Проблемы с EventBus

1. Сложно отследить поток данных

eventBus.emit('something:happened', data);

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

3. Утечки памяти

useEffect(() => {
  eventBus.on('event', handler);
  return () => eventBus.off('event', handler);
}, []);

Лучшие практики

  1. Используй строго типизированные события (TypeScript)
  2. Документируй все события в одном месте
  3. Отписывайся от событий при размонтировании
  4. Избегай цепочек событий
  5. Предпочитай Context или state management

Итог

EventBus нужен для:

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

В современном React часто вместо EventBus используют React Context для глобального состояния, Zustand или Redux для сложного состояния, useCallback и props для локальной коммуникации.

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

Для чего нужен EventBus? | PrepBro