From def3fe8dafc3d6ed3451a493e76842b7d2e8363c Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 17 Oct 2024 15:09:25 -0400 Subject: [PATCH] chore: add js docs for context mutator hook (#1045) Minor refactors, but mostly adding js-doc after https://github.com/open-feature/js-sdk/pull/1031. --------- Signed-off-by: Todd Baert --- .../react/src/{provider => common}/context.ts | 4 +- packages/react/src/common/index.ts | 4 ++ packages/react/src/context/index.ts | 1 + .../react/src/context/use-context-mutator.ts | 51 +++++++++++++++++++ .../react/src/evaluation/use-feature-flag.ts | 7 +-- packages/react/src/index.ts | 1 + packages/react/src/provider/index.ts | 1 - packages/react/src/provider/provider.tsx | 4 +- packages/react/src/provider/test-provider.tsx | 4 +- .../react/src/provider/use-context-mutator.ts | 36 ------------- .../src/provider/use-open-feature-client.ts | 2 +- .../src/provider/use-when-provider-ready.ts | 6 +-- packages/react/test/provider.spec.tsx | 2 +- 13 files changed, 69 insertions(+), 54 deletions(-) rename packages/react/src/{provider => common}/context.ts (90%) create mode 100644 packages/react/src/common/index.ts create mode 100644 packages/react/src/context/index.ts create mode 100644 packages/react/src/context/use-context-mutator.ts delete mode 100644 packages/react/src/provider/use-context-mutator.ts diff --git a/packages/react/src/provider/context.ts b/packages/react/src/common/context.ts similarity index 90% rename from packages/react/src/provider/context.ts rename to packages/react/src/common/context.ts index ce95f5a24..9e44801e7 100644 --- a/packages/react/src/provider/context.ts +++ b/packages/react/src/common/context.ts @@ -1,7 +1,7 @@ import type { Client } from '@openfeature/web-sdk'; import React from 'react'; -import type { NormalizedOptions, ReactFlagEvaluationOptions} from '../common/options'; -import { normalizeOptions } from '../common/options'; +import type { NormalizedOptions, ReactFlagEvaluationOptions} from '../common'; +import { normalizeOptions } from '../common'; /** * The underlying React context. diff --git a/packages/react/src/common/index.ts b/packages/react/src/common/index.ts new file mode 100644 index 000000000..a5b372459 --- /dev/null +++ b/packages/react/src/common/index.ts @@ -0,0 +1,4 @@ +export * from './context'; +export * from './is-equal'; +export * from './options'; +export * from './suspense'; diff --git a/packages/react/src/context/index.ts b/packages/react/src/context/index.ts new file mode 100644 index 000000000..6377bcc69 --- /dev/null +++ b/packages/react/src/context/index.ts @@ -0,0 +1 @@ +export * from './use-context-mutator'; diff --git a/packages/react/src/context/use-context-mutator.ts b/packages/react/src/context/use-context-mutator.ts new file mode 100644 index 000000000..420e923c6 --- /dev/null +++ b/packages/react/src/context/use-context-mutator.ts @@ -0,0 +1,51 @@ +import { useCallback, useContext, useRef } from 'react'; +import type { EvaluationContext } from '@openfeature/web-sdk'; +import { OpenFeature } from '@openfeature/web-sdk'; +import { Context } from '../common'; + +export type ContextMutationOptions = { + /** + * Mutate the default context instead of the domain scoped context applied at the ``. + * Note, if the `` has no domain specified, the default is used. + * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#manage-evaluation-context-for-domains|documentation} for more information. + * @default false + */ + defaultContext?: boolean; +}; + +export type ContextMutation = { + /** + * A function to set the desired context (see: {@link ContextMutationOptions} for details). + * There's generally no need to await the result of this function; flag evaluation hooks will re-render when the context is updated. + * This promise never rejects. + * @param updatedContext + * @returns Promise for awaiting the context update + */ + setContext: (updatedContext: EvaluationContext) => Promise; +}; + +/** + * Get function(s) for mutating the evaluation context associated with this domain, or the default context if `defaultContext: true`. + * See the {@link https://openfeature.dev/docs/reference/technologies/client/web/#targeting-and-context|documentation} for more information. + * @param {ContextMutationOptions} options options for the generated function + * @returns {ContextMutation} function(s) to mutate context + */ +export function useContextMutator(options: ContextMutationOptions = { defaultContext: false }): ContextMutation { + const { domain } = useContext(Context) || {}; + const previousContext = useRef(null); + + const setContext = useCallback(async (updatedContext: EvaluationContext) => { + if (previousContext.current !== updatedContext) { + if (!domain || options?.defaultContext) { + OpenFeature.setContext(updatedContext); + } else { + OpenFeature.setContext(domain, updatedContext); + } + previousContext.current = updatedContext; + } + }, [domain]); + + return { + setContext, + }; +} diff --git a/packages/react/src/evaluation/use-feature-flag.ts b/packages/react/src/evaluation/use-feature-flag.ts index cbcaea92a..05d6d8841 100644 --- a/packages/react/src/evaluation/use-feature-flag.ts +++ b/packages/react/src/evaluation/use-feature-flag.ts @@ -11,15 +11,12 @@ import { ProviderStatus, } from '@openfeature/web-sdk'; import { useEffect, useRef, useState } from 'react'; -import type { ReactFlagEvaluationOptions} from '../common/options'; -import { DEFAULT_OPTIONS, normalizeOptions } from '../common/options'; -import { suspendUntilReady } from '../common/suspense'; -import { useProviderOptions } from '../provider/context'; +import type { ReactFlagEvaluationOptions} from '../common'; +import { DEFAULT_OPTIONS, isEqual, normalizeOptions, suspendUntilReady, useProviderOptions } from '../common'; import { useOpenFeatureClient } from '../provider/use-open-feature-client'; import { useOpenFeatureClientStatus } from '../provider/use-open-feature-client-status'; import type { FlagQuery } from '../query'; import { HookFlagQuery } from './hook-flag-query'; -import { isEqual } from '../common/is-equal'; // This type is a bit wild-looking, but I think we need it. // We have to use the conditional, because otherwise useFlag('key', false) would return false, not boolean (too constrained). diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 34b1d7d1a..6690ab35a 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,5 +1,6 @@ export * from './evaluation'; export * from './query'; export * from './provider'; +export * from './context'; // re-export the web-sdk so consumers can access that API from the react-sdk export * from '@openfeature/web-sdk'; diff --git a/packages/react/src/provider/index.ts b/packages/react/src/provider/index.ts index a4ee0392b..5d29f9cd3 100644 --- a/packages/react/src/provider/index.ts +++ b/packages/react/src/provider/index.ts @@ -2,4 +2,3 @@ export * from './provider'; export * from './use-open-feature-client'; export * from './use-when-provider-ready'; export * from './test-provider'; -export * from './use-context-mutator'; \ No newline at end of file diff --git a/packages/react/src/provider/provider.tsx b/packages/react/src/provider/provider.tsx index 64f641649..02ebc13ad 100644 --- a/packages/react/src/provider/provider.tsx +++ b/packages/react/src/provider/provider.tsx @@ -1,8 +1,8 @@ import type { Client} from '@openfeature/web-sdk'; import { OpenFeature } from '@openfeature/web-sdk'; import * as React from 'react'; -import type { ReactFlagEvaluationOptions } from '../common/options'; -import { Context } from './context'; +import type { ReactFlagEvaluationOptions } from '../common'; +import { Context } from '../common'; type ClientOrDomain = | { diff --git a/packages/react/src/provider/test-provider.tsx b/packages/react/src/provider/test-provider.tsx index 38becaf2f..72746c764 100644 --- a/packages/react/src/provider/test-provider.tsx +++ b/packages/react/src/provider/test-provider.tsx @@ -7,7 +7,7 @@ import { OpenFeature } from '@openfeature/web-sdk'; import React from 'react'; -import type { NormalizedOptions } from '../common/options'; +import type { NormalizedOptions } from '../common'; import { OpenFeatureProvider } from './provider'; type FlagValueMap = { [flagKey: string]: JsonValue }; @@ -119,4 +119,4 @@ function mixInNoop(provider: Partial = {}) { (provider.metadata as unknown) = { name: TEST_PROVIDER }; } return provider; -} \ No newline at end of file +} diff --git a/packages/react/src/provider/use-context-mutator.ts b/packages/react/src/provider/use-context-mutator.ts deleted file mode 100644 index 86dd242d3..000000000 --- a/packages/react/src/provider/use-context-mutator.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useCallback, useContext, useRef } from 'react'; -import type { EvaluationContext } from '@openfeature/web-sdk'; -import { OpenFeature } from '@openfeature/web-sdk'; -import { Context } from './context'; - -/** - * - * A hook for accessing context mutating functions. - * - */ -export function useContextMutator({ - setGlobal -}: { - /** - * Apply changes to the global context instead of the domain scoped context applied at the React Provider - */ - setGlobal?: boolean; -} = {}) { - const { domain } = useContext(Context) || {}; - const previousContext = useRef(null); - - const setContext = useCallback(async (updatedContext: EvaluationContext) => { - if (previousContext.current !== updatedContext) { - if (!domain || setGlobal) { - OpenFeature.setContext(updatedContext); - } else { - OpenFeature.setContext(domain, updatedContext); - } - previousContext.current = updatedContext; - } - }, [domain]); - - return { - setContext, - }; -} diff --git a/packages/react/src/provider/use-open-feature-client.ts b/packages/react/src/provider/use-open-feature-client.ts index 9b395fedb..5fe3f7604 100644 --- a/packages/react/src/provider/use-open-feature-client.ts +++ b/packages/react/src/provider/use-open-feature-client.ts @@ -1,5 +1,5 @@ import React from 'react'; -import { Context } from './context'; +import { Context } from '../common'; import type { Client } from '@openfeature/web-sdk'; /** diff --git a/packages/react/src/provider/use-when-provider-ready.ts b/packages/react/src/provider/use-when-provider-ready.ts index 885b24a62..0c2f37daf 100644 --- a/packages/react/src/provider/use-when-provider-ready.ts +++ b/packages/react/src/provider/use-when-provider-ready.ts @@ -1,10 +1,8 @@ import { ProviderStatus } from '@openfeature/web-sdk'; -import type { ReactFlagEvaluationOptions} from '../common/options'; -import { DEFAULT_OPTIONS, normalizeOptions } from '../common/options'; -import { useProviderOptions } from './context'; import { useOpenFeatureClient } from './use-open-feature-client'; import { useOpenFeatureClientStatus } from './use-open-feature-client-status'; -import { suspendUntilReady } from '../common/suspense'; +import type { ReactFlagEvaluationOptions} from '../common'; +import { DEFAULT_OPTIONS, useProviderOptions, normalizeOptions, suspendUntilReady } from '../common'; type Options = Pick; diff --git a/packages/react/test/provider.spec.tsx b/packages/react/test/provider.spec.tsx index 0c8bd3581..8e8c84a7e 100644 --- a/packages/react/test/provider.spec.tsx +++ b/packages/react/test/provider.spec.tsx @@ -252,7 +252,7 @@ describe('OpenFeatureProvider', () => { })); const GlobalComponent = ({ name }: { name: string }) => { const flagValue = useStringFlagValue<'b' | 'a'>('globalFlagsHere', 'a'); - + return (