Skip to content

[BUG] Bad handling of a race condition between setContext and setProvider in React SDK #1331

Description

@yugisu-flux

Observed behavior

Custom provider's resolve methods never get called if OpenFeature.setProvider(provider) is being called after a OpenFeature.setContext({ ... }) call within the React tree. Please see the "Steps to reproduce" section for more details.

Expected Behavior

Flag evaluation hooks should properly react to provider changes.

Steps to reproduce

Consider the following generic initialization/usage setup:

import { OpenFeature, OpenFeatureProvider, useFlag, type EvaluationContext, type Provider, type ResolutionDetails } from "@openfeature/react-sdk";
import { useEffect, useState } from "react";

class CustomProvider implements Provider {
  readonly runsOn = "client";
  readonly metadata = { name: "custom-provider" };

  async initialize() {
    // noop
  }

  async onContextChange(oldContext: EvaluationContext, newContext: EvaluationContext) {
    // noop
  }

  resolveStringEvaluation(): ResolutionDetails<string> {
    console.log('This log will not show up in console until we click the state update button.');

    return {
      value: "Resolved string value",
      reason: "STATIC",
    };
  }
}

(async () => {
  // Some async init logic the `CustomProvider` is dependent upon.
  await new Promise<void>((resolve) => setTimeout(() => resolve(), 50));

  const provider = new CustomProvider();
  OpenFeature.setProvider(provider);
})();

function AppWithProviders() {
  useEffect(() => {
    OpenFeature.setContext({
      targetingKey: "alien",
    });
  }, []);

  return (
    <OpenFeatureProvider>
      <App />
    </OpenFeatureProvider>
  );
}

function App() {
  const flag = useFlag('test-flag', 'Default value')

  const [count, setCount] = useState(0);

  return <>
    {flag.value}
    <br />
    <button onClick={() => setCount(v => v + 1)}>
      force re-render
    </button>
  </>;
}

export default AppWithProviders;

On initial render, the app shows the following state:
Image

We expect the flag value to be resolved to "Resolved string value" from the CustomProvider@resolveStringEvaluation method.

However, if we force a component re-render (by clicking the button and increasing the counter):
Image


A more verbose reproduction app with async init / setcontext methods can be found here: https://codesandbox.io/p/sandbox/77yknf

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions