## This PR
- Added `useEffect` that runs on re-render to re-evaluate the flag value
- Only updates state if the resolved value actually changed (using
`isEqual` comparison)
- Used lazy initialization for `useState` to avoid redundant initial
evaluation
- Added `useCallback` memoization for event handlers
- Fixed `AbortController` scope issue
### Notes
This resolves a subtle issue where the provider state may update without
emitting a change event, leading to confusing results. The `useFlag`
hook sets the initial evaluated value in a `useState`. Since this wasn't
in a closure, this evaluation happened any time the component using the
hook rerendered but the result was essentially ignored. Adding a logging
hook shows that the current results but since this evaluation was made
in an existing `useState`, the result had no effect.
This resolves a subtle issue where the provider state may update without
emitting a change event, leading to stale flag values being displayed.
The `useFlag` hook was evaluating the flag on every re-render (as part
of the `useState` initialization), but because `useState` only uses its
initial value on the first render, these subsequent evaluations were
being discarded. This meant that even though the hook was fetching the
correct updated value from the provider on each re-render, it was
throwing that value away and continuing to display the stale cached
value.
Adding a logging hook would show the correct evaluation happening
(proving the provider had the updated value), but the UI would remain
stuck with the old value because the `useState` was ignoring the
re-evaluated result.
The fix ensures that these re-evaluations on re-render actually update
the component's state when the resolved value changes.
The key insight is that the evaluation WAS happening on every re-render
(due to how useState works), but React was discarding the result. Your
fix makes those evaluations actually matter by checking if the value
changed and updating state accordingly.
Original thread:
https://cloud-native.slack.com/archives/C06E4DE6S07/p1754508917397519
### How to test
I created a test that reproduced the issue, and it failed. I then
implemented the changes and verified that the test passed.
---------
Signed-off-by: Developer <developer@example.com>
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Developer <developer@example.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
## This PR
- adds an internal `use` polyfill
- refactors suspense support to maintain state across rerenders
### Notes
Previously, the Next.JS build process would timeout when using a
suspense flag hook. The reason for this is because the noop provider
(which is used during a build) is never ready. That meant that the
promise thrown to initiate suspense never resolved. To address this,
I've added global state using a weak map that's keyed off a provider. A
`useRef` won't help because the value is only retained if the component
renders successfully.
> [!NOTE]
> This unblocks suspense in our demo app but does not mean NextJS is
officially support (yet).
### How to test
I've added some tests and manually tested in the Toggle Shop.
---------
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->
## This PR
<!-- add the description of the PR here -->
exposes the internal open feature client status hook so developers can
act on non-ready resolved states
### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->
Fixes#1036
### Notes
<!-- any additional notes for this PR -->
### Follow-up Tasks
<!-- anything that is related to this PR but not done here should be
noted under this section -->
<!-- if there is a need for a new issue, please link it here -->
### How to test
<!-- if applicable, add testing instructions under this section -->
---------
Signed-off-by: William Chou <iam@willchou.dev>
Signed-off-by: Will Chou <w.chou06@gmail.com>
Co-authored-by: Lukas Reining <lukas.reining@codecentric.de>
## This PR
- renames the `shared` folder to `internal` to prevent accident exports
- export public option types
### Notes
This will make it easier for devs who need access to the option types.
---------
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Adds an improvement to the React SDK which supports re-renders if the
[flags
changed](https://open-feature.github.io/js-sdk/types/_openfeature_server_sdk.ConfigChangeEvent.html)
array from a provider event is falsy.
Since some providers have no knowledge of flags which are changed, this
allows them to support dynamic re-rendering by not defining this
property. If the prop is null/undefined, we diff all flags... If the
property is explicitly set to an empty array, that means no flags have
changed and the React SDK skips all diff checks.
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
📣 This was a draft for a while, but is now ready for review! 📣
This implements tracking as per spec, in the server, web, and react
SDKs.
I don't think the Angular or Nest SDKs need specific implementations,
but please advise (cc @luizgribeiro @lukas-reining).
Fixes: https://github.com/open-feature/js-sdk/issues/1033
Fixes: https://github.com/open-feature/js-sdk/issues/1034
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Inspired by [this
comment](https://github.com/open-feature/js-sdk/pull/1020#discussion_r1777829664)
I've added a lint rule to enforce `import type`, and some additional
package changes to add a `lint:fix`.
The only changes I made manually here is to add the lint rule, and the
package.json script. All the changes are auto-generated by the
`lint:fix`.
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
## This PR
- avoid re-resolving flags unaffected by a change event
### Notes
If the provider sends the change event payload, the React SDK uses the
list of change flags. If the list is missing or empty, all events are
triggered. This is a way to avoid sending unnecessary evaluation
telemetry data.
---------
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
I've been meaning to do this for a while... it just renames the
`client/` dir to `web/`. I think this is better because there will be
less confusion around the OpenFeature client object (which has it's own
dirs) and because it's more consistent with the associated artifact name
"web-sdk".
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Fixes an issues with the React `OpenFeatureTestProvider` where the
provider was not ready until the next event loop tick when the
`flagValueMap` was used.
Also removes the initialization in the client in-memory provider, since
it was only doing some un-needed validation inconsistent with the server
provider.
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Adds some testing utilities, specifically an
`<OpenFeatureTestProvider/>` react context provider.
See README for details.
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
## This PR
- replace the es2022 error cause with a custom implementation
- lower compilation target from es2022 to es2015
### Related Issues
Fixes#956
### Notes
The tests pass, but I still want to manually build and test the outputs
in a real application to ensure everything works as expected.
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
This PR:
- inverts the suspense defaults (we now do not suspend by default, you
have to add `suspend:true` in options)
- adds `useSuspenseFlag` (analogous to `useSuspenseXxx`) in other
libraries, which behaves the same as `useFlag` with `{ suspend: true }`
- updates README (specifically encourages use use "query-style" hooks
over type-specific hooks
- adds `@experimental` jsdoc marker to all suspense options and hooks
- associated tests
Things to consider:
- I did not add `useSuspense{Type}FlagValue` and
`useSuspense{Type}FlagDetails` hooks; we could do this if we wanted, but
IMO these are already not the primary APIs we want to push users toward
in react - we want them to use the generic `useFlag` and
`useSuspenseFlag` which return the react query interfaces.
Fixes: https://github.com/open-feature/js-sdk/issues/933
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
## This PR
- overloads the set provider methods to support defining context in the
web SDK
- updates the web sdk readme
## Related Issues
Fixes https://github.com/open-feature/js-sdk/issues/748
## Notes
I decided to only support setting context in the web SDK because it is
less valuable on the server and the expected behavior was less clear due
to `domains`.
The behavior may need to be spec'd out. An issue in the spec repo has
been created.
https://github.com/open-feature/spec/issues/219
---------
Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->
## This PR
<!-- add the description of the PR here -->
- introduces tests for `HookFlagQuery`
### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->
Resolves#915
### Notes
Placed these tests within `evaluation.spec.tsx`, happy to move them into
their own file if more appropriate
---------
Signed-off-by: Rowan Heptinstall <rowanheptinstall1@gmail.com>
Signed-off-by: Rowan Heptinstall <155630047+ro1hep@users.noreply.github.com>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
<!-- Please use this template for your pull request. -->
<!-- Please use the sections that you need and delete other sections -->
## This PR
<!-- add the description of the PR here -->
- Add an internal `useOpenFeatureClientStatus()` function to make it
slightly easier to keep track of the state of the current Client, and
know when to suspend or not.
- Replaces the `suspend()` function with a `suspendUntilReady()`
function. The `suspendUntilReady()` function fires immediatly during the
first render or when the component cannot render due to the client
having changed status.
- Put the Client status and the feature flag details inside React
states. This is required to make APIs like `startTransition` work
properly with the library
### Related Issues
<!-- add here the GitHub issue that this PR resolves if applicable -->
Fixes#920
### Notes
<!-- any additional notes for this PR -->
### Follow-up Tasks
- [x] Add more tests. Suspense and Transitions are very tricky. While
these changes don't seem to have broken any existing tests, we should
add new tests to make sure Suspense and Transitions continue to work
properly in the future.
- [ ] ~I think there might still be some reactivity issues with some
parts of the codebase. The client returned by `useOpenFeatureClient()`
didn't seem to update properly when the client changed, which made
`useOpenFeatureClientStatus()` not update properly.~ Out of the scope of
this PR
### How to test
<!-- if applicable, add testing instructions under this section -->
I built the application with `npm build`, then `npm pack`. I used the
modified code on a local project to see if it fixed the issues.
I built projects in CodeSanbox that demonstrate the issues.
For Suspense:
https://codesandbox.io/embed/openfeature-suspense-bug-5j7yll?fontsize=14&hidenavigation=1&theme=dark
For Transitions:
https://codesandbox.io/embed/openfeature-suspense-bug-forked-lqhyf3?fontsize=14&hidenavigation=1&theme=dark
---------
Signed-off-by: Tommy Josépovic <tommy.josepovic@gsoft.com>
- fixes issue where invocation hooks were not called in the React SDK
- adds associated tests
Fixes: https://github.com/open-feature/js-sdk/issues/914
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
- prompts a release of a non-experimental react-sdk
- updates README to point to React and NestJS SDKs
- removes version of peer deps from README (too brittle, point users to
`package.json`)
- adds an FAQ question
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
This PR:
- brings react-sdk test coverage from 0% to ~95%
- adds DOM-based testing (tests based on asserting DOM entity states)
- tests for query-style, basic, and detailed evaluation APIs
- tests for suspense functionality and re-rendering on context change
- tests for some util functions and hooks
- `renderHook` tests for non suspending hooks
- fixes a bug where `useWhenProviderReady` didn't cause re-render after
the provider is ready if suspense wasn't used (leading to an out-of-date
return value for provider readiness)

---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
When I was adding the general `suspend` option as @lukas-reining
suggested (a way to enable/disable all suspense features) I broke the
default options. I found this writing tests in a different branch.
Tests for the entire SDK including this are incoming shortly (today),
but this is a fix for the issue.
---------
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>