Skip to content

Conversation

@MattIPv4
Copy link
Contributor

@MattIPv4 MattIPv4 commented Oct 30, 2025

This PR

👋 Maybe I'm missing something, but useOpenFeatureClient currently returns an unstable client that is recreated every time the provider re-renders, if you pass a domain into the provider instead of a specific client.

This PR wraps the creation of that client when a domain is passed in useMemo, which should ensure that it is then stable for downstream usage via the useOpenFeatureClient hook.

As an example, due to client being unstable (combined with the shouldRunNow behaviour in the client), this logs ready every time the provider re-renders rather than just once when the client is actually ready.

  const client = useOpenFeatureClient();
  useEffect(() => {
    const ready = () => {
      console.log('ready');
    };

    client.addHandler(ProviderEvents.Ready, ready);
    return () => {
      client.removeHandler(ProviderEvents.Ready, ready);
    };
  }, [client]);

The closest workaround I've found currently is checking if the domain matches w/ useState + useEffect:

  const client = useOpenFeatureClient();

  const [stableClient, setStableClient] = useState(client);
  useEffect(() => {
    setStableClient((existing) => (existing.metadata.domain === client.metadata.domain ? existing : client));
  }, [client]);

  useEffect(() => {
    const ready = () => {
      console.log('ready');
    };

    stableClient.addHandler(ProviderEvents.Ready, ready);
    return () => {
      stableClient.removeHandler(ProviderEvents.Ready, ready);
    };
  }, [stableClient]);

@MattIPv4 MattIPv4 requested review from a team as code owners October 30, 2025 19:47
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @MattIPv4, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical stability issue within the React OpenFeature provider. Previously, when a domain was passed instead of an explicit client, the client instance was inadvertently recreated on every re-render, leading to an unstable client. The core change introduces memoization to ensure a consistent client instance, thereby improving performance and predictability for components consuming the OpenFeature client.

Highlights

  • Client Stability: The OpenFeatureProvider now memoizes the OpenFeature client using React.useMemo when a domain is provided, ensuring a stable client instance across re-renders.
  • Performance Optimization: This change prevents unnecessary re-creation of the client, addressing an issue where useOpenFeatureClient would return an unstable client, leading to potential performance issues or unexpected behavior in consuming components.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly addresses a performance issue by memoizing the OpenFeature client instance within the OpenFeatureProvider. This prevents the client from being recreated on every render, providing stability for downstream hooks like useOpenFeatureClient. The change is well-implemented. I've added one suggestion to further enhance performance by also memoizing the context provider's value object, which would prevent unnecessary re-renders for all context consumers.

Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wow, great catch. I wonder if it's worth adding a test to ensure we get the same client after a rerender.

@toddbaert
Copy link
Member

Great find and fix! Can you add a test? You can probably just assert the object refs are equal. Try to make sure the test fails before your change.

@MattIPv4
Copy link
Contributor Author

Yeh, can do, give me a bit to actually get this setup locally (I did this patch via GitHub's UI).

@toddbaert
Copy link
Member

This is probably causing some memory/handler leaks. Maybe hard to spot because of the nature of frontend, but it almost certainly must be.

Copy link
Member

@lukas-reining lukas-reining left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good catch, thanks!
Generally the recreation would be fine, but the handlers are the one thing that leaks.

@toddbaert toddbaert enabled auto-merge October 30, 2025 20:54
@toddbaert toddbaert added this pull request to the merge queue Oct 30, 2025
Merged via the queue into open-feature:main with commit 405d61d Oct 30, 2025
7 of 10 checks passed
beeme1mr pushed a commit that referenced this pull request Oct 31, 2025
## This PR

Failure first appeared in
https://github.com/open-feature/js-sdk/actions/runs/18692818359/job/53302526714
-- noticed it locally while adding the test in #1276, and then saw the
failure on main when my PR was merged.

Signed-off-by: MattIPv4 <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request Oct 31, 2025
🤖 I have created a release *beep* *boop*
---


##
[1.0.2](react-sdk-v1.0.1...react-sdk-v1.0.2)
(2025-10-31)


### 🐛 Bug Fixes

* memoize React client to provide stability
([#1276](#1276))
([405d61d](405d61d))


### 🧹 Chore

* mention debounce hook in react/ng docs
([#1272](#1272))
([27666b8](27666b8))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Signed-off-by: OpenFeature Bot <[email protected]>
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