Skip to content

Commit 76ff504

Browse files
[FSSDK-12294] shared hook addition
1 parent 86849cc commit 76ff504

4 files changed

Lines changed: 40 additions & 22 deletions

File tree

src/hooks/useDecide.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useCallback, useEffect, useMemo, useState } from 'react';
18-
import { useSyncExternalStore } from 'use-sync-external-store/shim';
17+
import { useEffect, useMemo, useState } from 'react';
1918
import type { OptimizelyDecideOption, OptimizelyDecision } from '@optimizely/optimizely-sdk';
2019

2120
import { useOptimizelyContext } from './useOptimizelyContext';
21+
import { useProviderState } from './useProviderState';
2222
import { useStableArray } from './useStableArray';
2323

2424
export interface UseDecideConfig {
@@ -43,12 +43,7 @@ export type UseDecideResult =
4343
export function useDecide(flagKey: string, config?: UseDecideConfig): UseDecideResult {
4444
const { store, client } = useOptimizelyContext();
4545
const decideOptions = useStableArray(config?.decideOptions);
46-
// --- General state subscription ---
47-
// store.getState() returns a new object on every state change,
48-
// so Object.is comparison works naturally.
49-
const subscribeState = useCallback((onStoreChange: () => void) => store.subscribe(onStoreChange), [store]);
50-
const getStateSnapshot = useCallback(() => store.getState(), [store]);
51-
const state = useSyncExternalStore(subscribeState, getStateSnapshot, getStateSnapshot);
46+
const state = useProviderState(store);
5247

5348
// --- Forced decision subscription ---
5449
// Forced decisions don't change store state, so we use a version counter

src/hooks/useDecideAll.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useCallback, useEffect, useMemo, useState } from 'react';
18-
import { useSyncExternalStore } from 'use-sync-external-store/shim';
17+
import { useEffect, useMemo, useState } from 'react';
1918

2019
import { useOptimizelyContext } from './useOptimizelyContext';
20+
import { useProviderState } from './useProviderState';
2121
import { useStableArray } from './useStableArray';
2222
import type { UseDecideConfig } from './useDecide';
2323
import type { UseDecideMultiResult } from './useDecideForKeys';
@@ -34,11 +34,7 @@ import type { UseDecideMultiResult } from './useDecideForKeys';
3434
export function useDecideAll(config?: UseDecideConfig): UseDecideMultiResult {
3535
const { store, client } = useOptimizelyContext();
3636
const decideOptions = useStableArray(config?.decideOptions);
37-
38-
// --- General state subscription ---
39-
const subscribeState = useCallback((onStoreChange: () => void) => store.subscribe(onStoreChange), [store]);
40-
const getStateSnapshot = useCallback(() => store.getState(), [store]);
41-
const state = useSyncExternalStore(subscribeState, getStateSnapshot, getStateSnapshot);
37+
const state = useProviderState(store);
4238

4339
// --- Forced decision subscription — any flag key ---
4440
const [fdVersion, setFdVersion] = useState(0);

src/hooks/useDecideForKeys.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { useCallback, useEffect, useMemo, useState } from 'react';
18-
import { useSyncExternalStore } from 'use-sync-external-store/shim';
17+
import { useEffect, useMemo, useState } from 'react';
1918
import type { OptimizelyDecision } from '@optimizely/optimizely-sdk';
2019

2120
import { useOptimizelyContext } from './useOptimizelyContext';
21+
import { useProviderState } from './useProviderState';
2222
import { useStableArray } from './useStableArray';
2323
import type { UseDecideConfig } from './useDecide';
2424

@@ -42,11 +42,7 @@ export function useDecideForKeys(flagKeys: string[], config?: UseDecideConfig):
4242
const { store, client } = useOptimizelyContext();
4343
const stableKeys = useStableArray(flagKeys);
4444
const decideOptions = useStableArray(config?.decideOptions);
45-
46-
// --- General state subscription ---
47-
const subscribeState = useCallback((onStoreChange: () => void) => store.subscribe(onStoreChange), [store]);
48-
const getStateSnapshot = useCallback(() => store.getState(), [store]);
49-
const state = useSyncExternalStore(subscribeState, getStateSnapshot, getStateSnapshot);
45+
const state = useProviderState(store);
5046

5147
// --- Forced decision subscription — per-key with shared version counter ---
5248
const [fdVersion, setFdVersion] = useState(0);

src/hooks/useProviderState.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Copyright 2026, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { useCallback } from 'react';
18+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
19+
import type { ProviderState } from '../provider/index';
20+
import type { ProviderStateStore } from '../provider/index';
21+
22+
/**
23+
* Subscribes to the `ProviderStateStore` via `useSyncExternalStore`
24+
* and returns the current provider state.
25+
*/
26+
export function useProviderState(store: ProviderStateStore): ProviderState {
27+
const subscribeState = useCallback((onStoreChange: () => void) => store.subscribe(onStoreChange), [store]);
28+
const getStateSnapshot = useCallback(() => store.getState(), [store]);
29+
30+
return useSyncExternalStore(subscribeState, getStateSnapshot, getStateSnapshot);
31+
}

0 commit comments

Comments
 (0)