11import { UnleashClient , type IConfig } from 'unleash-proxy-client' ;
22import { FlagProvider as DefaultFlagProvider } from '@unleash/proxy-client-react' ;
3- import { initUnleashToolbar } from '../index' ;
3+ import { wrapUnleashClient , ToolbarStateManager , UnleashToolbar } from '../index' ;
44import type { InitToolbarOptions } from '../types' ;
5- import React , { useRef } from 'react' ;
5+ import React , { useRef , useEffect } from 'react' ;
66
77/**
88 * Base props shared by both config and client variants
@@ -121,27 +121,66 @@ export function UnleashToolbarProvider({
121121 ) ;
122122 }
123123
124- // Wrap client immediately on first render (not in useEffect)
125- // Use a lazy initializer to ensure it only runs once
124+ const toolbarRef = useRef < any > ( null ) ;
125+ const stateManagerRef = useRef < any > ( null ) ;
126+
127+ // Wrap client synchronously (idempotent, captures flag evaluations)
128+ // This ensures the wrapper intercepts all flag evaluations from the first render
126129 const wrappedClientRef = useRef < UnleashClient | null > ( null ) ;
127130
128131 if ( wrappedClientRef . current === null ) {
129- // Only initialize on client side (not during SSR)
130- if ( typeof window !== 'undefined' ) {
131- // Create client from config if provided
132- const unleashClient = client || new UnleashClient ( config ! ) ;
133-
134- // Skip toolbar initialization if toolbarOptions is explicitly undefined (production mode)
135- if ( toolbarOptions === undefined ) {
136- wrappedClientRef . current = unleashClient ;
137- } else {
138- wrappedClientRef . current = initUnleashToolbar ( unleashClient , toolbarOptions ) ;
132+ const unleashClient = client || new UnleashClient ( config ! ) ;
133+
134+ if ( toolbarOptions !== undefined ) {
135+ // Create state manager that will be shared between wrapper and toolbar
136+ const storageMode = toolbarOptions . storageMode || 'local' ;
137+ const storageKey = toolbarOptions . storageKey || 'unleash-toolbar-state' ;
138+ const sortAlphabetically = toolbarOptions . sortAlphabetically || false ;
139+ const stateManager = new ToolbarStateManager ( storageMode , storageKey , sortAlphabetically ) ;
140+ stateManagerRef . current = stateManager ;
141+
142+ if ( toolbarOptions . enableCookieSync ) {
143+ stateManager . enableCookieSync ( ) ;
139144 }
145+
146+ // Wrap client synchronously with shared state manager
147+ wrappedClientRef . current = wrapUnleashClient ( unleashClient , stateManager ) ;
140148 } else {
141- // SSR: create unwrapped client
142- wrappedClientRef . current = client || new UnleashClient ( config ! ) ;
149+ // no toolbar - just use original client
150+ wrappedClientRef . current = unleashClient ;
143151 }
144152 }
153+
154+ // Create toolbar UI in useEffect (StrictMode-safe)
155+ useEffect ( ( ) => {
156+ // Only create toolbar if:
157+ // 1. toolbarOptions is defined
158+ // 2. No toolbar ref yet
159+ // 3. State manager exists
160+ // 4. Wrapped client doesn't already have a toolbar (prevents StrictMode duplicates)
161+ if ( toolbarOptions !== undefined &&
162+ ! toolbarRef . current &&
163+ stateManagerRef . current &&
164+ ! ( wrappedClientRef . current as any ) ?. __toolbar ) {
165+ // Create toolbar with the same state manager used for wrapping
166+ const toolbar = new UnleashToolbar ( stateManagerRef . current , wrappedClientRef . current as any , toolbarOptions ) ;
167+ ( wrappedClientRef . current as any ) . __toolbar = toolbar ;
168+ toolbarRef . current = toolbar ;
169+
170+ // Expose globally
171+ if ( typeof window !== 'undefined' ) {
172+ ( window as any ) . unleashToolbar = toolbar ;
173+ }
174+ }
175+
176+ // Cleanup toolbar on unmount
177+ return ( ) => {
178+ if ( toolbarRef . current && typeof toolbarRef . current . destroy === 'function' ) {
179+ toolbarRef . current . destroy ( ) ;
180+ toolbarRef . current = null ;
181+ }
182+ } ;
183+ } , [ ] ) ;
145184
146185 return (
147186 < FlagProvider
@@ -152,4 +191,4 @@ export function UnleashToolbarProvider({
152191 { children }
153192 </ FlagProvider >
154193 ) ;
155- }
194+ }
0 commit comments