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?

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.

3 participants