Skip to content

Conversation

@jonathannorris
Copy link
Member

Add Context Change Listeners for Flag Subscriptions - Proposal

Overview

Implements context change listeners for the web SDK, addressing open-feature/spec#346. Provides a type-safe way to subscribe to flag value updates when evaluation context changes.

Problem

The current Events API requires significant boilerplate to track individual flag values. This PR adds a convenient API allowing developers to subscribe directly to flag changes with minimal code.

Solution

Two approaches for subscribing to context changes are implemented as examples for discussion:

1. EvaluationDetails Subscriptions

Enhanced EvaluationDetails objects returned from evaluation methods include an onContextChanged method. The callback is invoked whenever the context changes.

const unsubscribe = client.getBooleanDetails('feature-enabled', false)
                          .onContextChanged((newDetails, oldDetails) => {
  // Called on each context change
  console.log(`Flag updated: ${newDetails.value}`);
});

2. Client-Level Subscriptions

Type-specific methods directly on the client for subscribing to context changes. Performs an initial flag evaluation and invokes the callback immediately, then again whenever the context changes.

const client = OpenFeature.getClient();
const unsubscribe = client.onBooleanContextChanged(
  'feature-enabled',
  false,
  (newDetails, oldDetails) => {
    // Called immediately with initial value, then on each context change
    updateUI(newDetails.value);
  }
);

Implementation

  • EvaluationDetailsWithSubscription - Wraps EvaluationDetails with subscription capability
  • ContextChangeSubscriptions interface - Groups subscription methods
  • evaluateWithSubscription helper - DRY wrapper for evaluation results
  • Closure-based cleanup - Consistent with existing SDK patterns

Important: Providers do NOT need to implement ContextChanged events. The SDK automatically emits these events after context changes via setContext(), making this implementation reliable and provider-agnostic.

Handler Management: Uses closure-based approach consistent with existing SDK patterns. A WeakMap-based subscription registry was considered for subscription introspection/bulk operations but would diverge from current patterns and add complexity.

Usage Example

import { useEffect, useState } from 'react';
import { OpenFeature } from '@openfeature/web-sdk';

function FeatureComponent() {
  const [featureEnabled, setFeatureEnabled] = useState(false);
  const client = OpenFeature.getClient();

  useEffect(() => {
    const details = client.getBooleanDetails('new-feature', false);
    setFeatureEnabled(details.value); // Set initial value

    // Callback is invoked on context changes
    const unsubscribe = details.onContextChanged((newDetails) => {
      setFeatureEnabled(newDetails.value);
    });

    return unsubscribe;
  }, []);

  return <div>{featureEnabled ? 'Feature Active' : 'Feature Disabled'}</div>;
}

Related Issues

Addresses open-feature/spec#346

@bencehornak
Copy link

Thanks for the PoC, @jonathannorris! The first thing coming to my mind regarding the proposal is that it is reacting to variable value changes caused by context changes, but not to the ones caused by the provider backend (i.e. when an admin flips a flag or when a scheduled feature activation is due). This second one is a much more fundamental change, because providers also need to implement some server-to-client communication or polling to their own backends, but this is the way to support on-the-fly variable updates.

You and your colleagues implemented this in the DevCycle Android SDK with SSE, that's inspired me to open the referenced ticket in the spec repo:

https://github.com/DevCycleHQ/android-client-sdk/blob/b6794ab8a88f6ee447b8caf3c2ce92f10099d7f5/android-client-sdk/src/main/java/com/devcycle/sdk/android/api/DevCycleClient.kt#L145-L170

https://github.com/DevCycleHQ/android-client-sdk/blob/b6794ab8a88f6ee447b8caf3c2ce92f10099d7f5/android-client-sdk/src/main/java/com/devcycle/sdk/android/model/Variable.kt#L94-L112

So regarding the proposed API: as the author of the FeatureComponent based on your example I'd expect to be able to subscribe to variable changes no matter if they were caused by context changes or the provider. What do you think about making the subscribe methods more generic by removing the word 'context' from them (e.g. details.onChanged() or client.onBooleanChanged() and expanding the scope of the PoC to handle both cases?

@lukas-reining
Copy link
Member

lukas-reining commented Nov 8, 2025

Hey @jonathannorris, as discusses in the community meeting and described by @bencehornak, I think we would not only want to react to context changes but also to configuration changed.
In the static context SDKs, when the context is changed, the SDK calls the providers onContextChange method and the provider then typically fetches the flags. Once this is done, a configuration changed event will be emitted. [1]
This does not include the events where the provider is informed about a change by the backend which would be configuration changed.

What do you think about making the subscribe methods more generic by removing the word 'context' from them (e.g. details.onChanged() or client.onBooleanChanged() and expanding the scope of the PoC to handle both cases?

This makes sense to me @bencehornak. But we also have examples where we only want to react to one type of event.
E.g. we have the example in Angular [2] and React [3].
I think we should have this here too. It seems like the easiest way of implementing this would be to add optional parameters to the details.onChanged() or client.onBooleanChanged() methods.

This second one is a much more fundamental change, because providers also need to implement some server-to-client communication or polling to their own backends, but this is the way to support on-the-fly variable updates.

@bencehornak I do not see this as a problem, this is the case today too. If providers have a way to emit config changes they get emitted, otherwise the event will simply never trigger.
This happens in the React and Angular cases already today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants