JavaScriptProviderPatternSnippet

以下のコードは JavaScriptRiverpodのようなProviderPatternを実装した例である。
このコードを概略↓ノートはJavaScriptで作成するProviderPattern解剖図にて。

class ProviderContainer {
    constructor() {
        if(!ProviderContainer.instance){
            ProviderContainer.instance = this;
        }
        
        this._providers = new Map();
        this._dependencies = new Map();
        this._listeners = new Map();
 
        return ProviderContainer.instance;
    }
 
    createProvider(createFn) {
        const provider = new ProviderCore(
            createFn,
            null,
            false
        );
 
        return provider;
    }
 
    read(provider) {
        if (!provider._isInitialized) {
            const ref = this._createRef(provider);
 
            provider._value = provider._create(ref);
            provider._isInitialized = true;
        }
 
        return provider._value;
    }
 
    watch(provider, listener) {
        if (!this._listeners.has(provider)) {
            this._listeners.set(provider, new Set());
        }
 
        this._listeners.get(provider).add(listener);
 
        listener(this.read(provider));
 
        return () => {
            this._listeners.get(provider).delete(listener);
        };
    }
 
    update(provider, updateFn) {
        const currentValue = this.read(provider);
        const newValue = updateFn(currentValue);
 
        provider._value = newValue;
        provider._isInitialized = true;
 
        this._notifyListeners(provider, newValue);
    }
 
    _notifyListeners(provider, newValue) {
        const listeners = this._listeners.get(provider);
        if (listeners) {
            listeners.forEach(listener => listener(newValue));
        }
    }
 
    _createRef(currentProvider) {
        const ref = {
            watch: (otherProvider) => {
                return this.read(otherProvider);
            },
            update: (updateFn) => {
                this.update(currentProvider, updateFn);
            }
        };
 
        return ref;
    }
}
 
class ProviderCore {
    constructor(createFn) {
        this._create = createFn;
        this._value = null;
        this._isInitialized = false;
    }
}
 
// コンテナの作成
const PROVIDER_COTAINER = new ProviderContainer();
 
const container = new ProviderContainer();
 
// 数値プロバイダーの作成
const counterProvider = container.createProvider((ref) => {   
	return 0;
});
 
// ユーザープロバイダーの作成
const userProvider = container.createProvider((ref) => {
	return { name: '太郎', age: 30 };
});
 
// 派生プロバイダーの例
const userNameProvider = container.createProvider((ref) => {
    const user = ref.watch(userProvider);
    return user.name;
 });
 
// 値の読み取り
console.log(container.read(counterProvider)); // 0
 
// 値の監視
const unsubscribe = container.watch(counterProvider, (value) => {
    console.log('値の変更を検知:', value);
});
 
// 値の更新
container.update(counterProvider, (currentValue) => currentValue + 1);
 
//派生プロバイダーの使用
console.log(container.read(userNameProvider));// '太郎'