Observer паттерн

Observer (наблюдатель) — это поведенческий паттерн проектирования, который позволяет объектам оповещать другие объекты об изменениях своего состояния. В контексте Preact данный паттерн применяется для организации реактивного взаимодействия компонентов и управления состоянием, особенно в случаях, когда несколько компонентов зависят от одних и тех же данных.


Основная концепция

Паттерн Observer состоит из двух ключевых ролей:

  • Subject (Субъект) — объект, который хранит состояние и уведомляет подписчиков об изменениях.
  • Observer (Наблюдатель) — объект, который подписан на изменения субъекта и реагирует на них.

Классическая схема работы: субъект изменяет своё состояние и вызывает метод уведомления всех подписанных наблюдателей. Наблюдатели выполняют собственную логику обновления.


Реализация в Preact

Preact не включает встроенный Observer, однако реактивное поведение можно организовать с использованием хуков, таких как useState, useReducer и useEffect, или сторонних библиотек, реализующих реактивное состояние (например, rxjs, valtio, mobx).

Пример простого Observer через хуки
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';

class Subject {
    constructor() {
        this.observers = [];
        this.state = 0;
    }

    subscribe(observer) {
        this.observers.push(observer);
    }

    unsubscribe(observer) {
        this.observers = this.observers.filter(obs => obs !== observer);
    }

    setState(newState) {
        this.state = newState;
        this.notify();
    }

    notify() {
        this.observers.forEach(observer => observer(this.state));
    }
}

const Counter = ({ subject }) => {
    const [count, setCount] = useState(subject.state);

    useEffect(() => {
        const observer = (newState) => setCount(newState);
        subject.subscribe(observer);
        return () => subject.unsubscribe(observer);
    }, [subject]);

    return (
        <div>
            <p>Count: {count}</p>
            <button onCl ick={() => subject.setState(subject.state + 1)}>Increment</button>
        </div>
    );
};

// Использование
const subject = new Subject();

const App = () => (
    <div>
        <Counter subject={subject} />
        <Counter subject={subject} />
    </div>
);

В этом примере два компонента Counter подписаны на один и тот же subject. При изменении состояния subject оба компонента обновляют отображение автоматически.


Преимущества применения Observer в Preact

  • Реактивное обновление — все подписанные компоненты автоматически получают новые данные.
  • Разделение ответственности — компонент не управляет состоянием напрямую, а лишь реагирует на изменения.
  • Гибкость и масштабируемость — можно подключать новые наблюдатели без изменения логики субъекта.

Взаимодействие с Preact Hooks

Паттерн Observer в Preact органично сочетается с хуками:

  • useState для хранения локального состояния.
  • useEffect для подписки и отписки от изменений.
  • useReducer для управления сложными состояниями с реактивной логикой.

Пример использования useReducer для управления глобальным состоянием с Observer:

import { h } from 'preact';
import { useReducer, useEffect } from 'preact/hooks';

const initialState = { count: 0 };
const subject = {
    observers: [],
    state: initialState,
    subscribe(obs) { this.observers.push(obs); },
    unsubscribe(obs) { this.observers = this.observers.filter(o => o !== obs); },
    notify() { this.observers.forEach(obs => obs(this.state)); },
    dispatch(action) {
        switch(action.type) {
            case 'increment': this.state = { count: this.state.count + 1 }; break;
            case 'decrement': this.state = { count: this.state.count - 1 }; break;
        }
        this.notify();
    }
};

const Counter = () => {
    const [state, setState] = useReducer((_, newState) => newState, subject.state);

    useEffect(() => {
        const observer = (newState) => setState(newState);
        subject.subscribe(observer);
        return () => subject.unsubscribe(observer);
    }, []);

    return (
        <div>
            <p>Count: {state.count}</p>
            <button onCl ick={() => subject.dispatch({ type: 'increment' })}>+</button>
            <button onCl ick={() => subject.dispatch({ type: 'decrement' })}>-</button>
        </div>
    );
};

Расширенные сценарии

  • Глобальное состояние приложения: Observer позволяет создать реактивный стор без сторонних библиотек, подходящий для небольших приложений.
  • События и уведомления: можно использовать паттерн для управления событиями, например, сообщений в чате или обновлений данных с сервера.
  • Связь между компонентами: компоненты, находящиеся на разных уровнях иерархии, могут синхронизировать состояние через общий субъект.

Ключевые моменты

  • Observer разделяет источник данных и их потребителей.
  • В Preact подписка и отписка обычно реализуются через useEffect.
  • Для сложных приложений часто используют готовые реактивные библиотеки, интегрируемые с Preact.
  • Паттерн повышает модульность и облегчает тестирование компонентов.