@posthog/react 1.9.1 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,67 @@ describe('feature flag hooks', () => {
115
115
  expect(result.current).toEqual(expected)
116
116
  })
117
117
 
118
+ describe('useFeatureFlagEnabled defaultValue', () => {
119
+ // A client whose flags have loaded but where isFeatureEnabled returns undefined,
120
+ // i.e. the flag is absent from the payload. The default beforeEach mock coerces
121
+ // every result with `!!`, so it can never produce the undefined we need here.
122
+ function renderWithUnknownFlag() {
123
+ const client = {
124
+ isFeatureEnabled: () => undefined,
125
+ onFeatureFlags: () => () => {},
126
+ featureFlags: {
127
+ hasLoadedFlags: true,
128
+ } as unknown as PostHog['featureFlags'],
129
+ } as unknown as PostHog
130
+
131
+ // eslint-disable-next-line react/display-name
132
+ return ({ children }: { children: React.ReactNode }) => (
133
+ <PostHogProvider client={client}>{children}</PostHogProvider>
134
+ )
135
+ }
136
+
137
+ it.each([
138
+ [false as boolean | undefined, false],
139
+ [true as boolean | undefined, true],
140
+ [undefined, undefined],
141
+ ])('returns %s for an unknown flag when defaultValue is %s', (defaultValue, expected) => {
142
+ const { result } = renderHook(() => useFeatureFlagEnabled('missing', defaultValue as boolean), {
143
+ wrapper: renderWithUnknownFlag(),
144
+ })
145
+ expect(result.current).toBe(expected)
146
+ })
147
+
148
+ it('prefers the real flag value over the default', () => {
149
+ const { result } = renderHook(() => useFeatureFlagEnabled('example_feature_false', true), {
150
+ wrapper: renderProvider,
151
+ })
152
+ expect(result.current).toBe(false)
153
+ })
154
+
155
+ it('applies the default in the bootstrap branch when the flag is absent', () => {
156
+ const client = {
157
+ isFeatureEnabled: () => undefined,
158
+ onFeatureFlags: () => () => {},
159
+ config: {
160
+ bootstrap: {
161
+ featureFlags: { other_flag: true },
162
+ },
163
+ },
164
+ featureFlags: {
165
+ hasLoadedFlags: false,
166
+ } as unknown as PostHog['featureFlags'],
167
+ } as unknown as PostHog
168
+
169
+ // eslint-disable-next-line react/display-name
170
+ const wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
171
+ <PostHogProvider client={client}>{children}</PostHogProvider>
172
+ )
173
+
174
+ const { result } = renderHook(() => useFeatureFlagEnabled('my_flag', false), { wrapper })
175
+ expect(result.current).toBe(false)
176
+ })
177
+ })
178
+
118
179
  describe('useFeatureFlagResult', () => {
119
180
  describe('bootstrap fallback', () => {
120
181
  function renderWithBootstrap(
@@ -2,7 +2,25 @@ import { useContext, useEffect, useState } from 'react'
2
2
  import { PostHogContext } from '../context'
3
3
  import { isUndefined } from '../utils/type-utils'
4
4
 
5
- export function useFeatureFlagEnabled(flag: string): boolean | undefined {
5
+ /**
6
+ * Check whether a feature flag is enabled for the current user.
7
+ *
8
+ * Returns `undefined` while flags are still loading or when the flag is absent, so callers can
9
+ * distinguish "not known yet" from "disabled".
10
+ *
11
+ * @param flag Key of the feature flag.
12
+ * @returns Whether the flag is enabled, or `undefined` if not yet loaded or not found.
13
+ */
14
+ export function useFeatureFlagEnabled(flag: string): boolean | undefined
15
+ /**
16
+ * Check whether a feature flag is enabled for the current user.
17
+ *
18
+ * @param flag Key of the feature flag.
19
+ * @param defaultValue Returned instead of `undefined` while flags are loading or when the flag is absent.
20
+ * @returns Whether the flag is enabled, falling back to `defaultValue` when the value is unknown.
21
+ */
22
+ export function useFeatureFlagEnabled(flag: string, defaultValue: boolean): boolean
23
+ export function useFeatureFlagEnabled(flag: string, defaultValue?: boolean): boolean | undefined {
6
24
  const { client, bootstrap } = useContext(PostHogContext)
7
25
 
8
26
  const [featureEnabled, setFeatureEnabled] = useState<boolean | undefined>(() => client.isFeatureEnabled(flag))
@@ -17,8 +35,9 @@ export function useFeatureFlagEnabled(flag: string): boolean | undefined {
17
35
 
18
36
  // if the client is not loaded yet, check if we have a bootstrapped value and then true/false it
19
37
  if (!client?.featureFlags?.hasLoadedFlags && bootstrap?.featureFlags) {
20
- return isUndefined(bootstrapped) ? undefined : !!bootstrapped
38
+ return isUndefined(bootstrapped) ? defaultValue : !!bootstrapped
21
39
  }
22
40
 
23
- return featureEnabled
41
+ // while the flag value is unknown (flags not loaded, or the flag is absent), fall back to defaultValue
42
+ return isUndefined(featureEnabled) ? defaultValue : featureEnabled
24
43
  }