@@ -23,7 +23,6 @@ import {
2323import {
2424 deepEqual ,
2525 getCookie ,
26- serializeVisitorKeyCookie ,
2726 combineRawFlagsWithDefaultFlags ,
2827 ObjectMap ,
2928 has ,
@@ -165,134 +164,139 @@ function canSettle<F extends Flags>(state: State<F>) {
165164 ) ;
166165}
167166
168- /**
169- * The reducer returns a tuple of [state, effects].
170- *
171- * effects is an array of effects to execute. The emitted effects are then later
172- * executed in another hook.
173- *
174- * This pattern is basically a hand-rolled version of
175- * https://github.com/davidkpiano/useEffectReducer
176- *
177- * We use a hand-rolled version to keep the size of this package minimal.
178- */
179- function reducer < F extends Flags > (
180- tuple : readonly [ State < F > , Effect [ ] ] ,
181- action : Action < F >
182- ) : readonly [ State < F > , Effect [ ] ] {
183- const [ state /* and effects */ ] = tuple ;
184-
185- switch ( action . type ) {
186- case "evaluate" : {
187- const cachedOutcome = cache . get < SuccessOutcome < F > > ( action . input ) ;
188-
189- const [ effects , pending ] = createFetchEffects < F > (
190- action . input ,
191- state . pending
192- ) ;
193-
194- // action.input will always differ from state.input, because we do not
195- // dispatch "evaluate" otherwise
196- return [
197- {
198- name : "evaluating" ,
199- input : action . input ,
200- cachedOutcome,
201- pending,
202- } ,
203- effects ,
204- ] ;
205- }
206- case "revalidate" : {
207- if ( state . name === "empty" ) return tuple ;
208-
209- const input = action . input || state . input ;
210- const [ effects , pending ] = createFetchEffects < F > ( input , state . pending ) ;
211-
212- if ( state . name === "succeeded" )
167+ function createReducer < F extends Flags > ( config : FullConfiguration < F > ) {
168+ /**
169+ * The reducer returns a tuple of [state, effects].
170+ *
171+ * effects is an array of effects to execute. The emitted effects are then later
172+ * executed in another hook.
173+ *
174+ * This pattern is basically a hand-rolled version of
175+ * https://github.com/davidkpiano/useEffectReducer
176+ *
177+ * We use a hand-rolled version to keep the size of this package minimal.
178+ */
179+ return function reducer < F extends Flags > (
180+ tuple : readonly [ State < F > , Effect [ ] ] ,
181+ action : Action < F >
182+ ) : readonly [ State < F > , Effect [ ] ] {
183+ const [ state /* and effects */ ] = tuple ;
184+
185+ switch ( action . type ) {
186+ case "evaluate" : {
187+ const cachedOutcome = cache . get < SuccessOutcome < F > > ( action . input ) ;
188+
189+ const [ effects , pending ] = createFetchEffects < F > (
190+ action . input ,
191+ state . pending
192+ ) ;
193+
194+ // action.input will always differ from state.input, because we do not
195+ // dispatch "evaluate" otherwise
213196 return [
214197 {
215- name : "revalidating-after-success" ,
216- input : state . input ,
217- outcome : state . outcome ,
218- cachedOutcome : state . cachedOutcome ,
198+ name : "evaluating" ,
199+ input : action . input ,
200+ cachedOutcome,
219201 pending,
220202 } ,
221203 effects ,
222204 ] ;
205+ }
206+ case "revalidate" : {
207+ if ( state . name === "empty" ) return tuple ;
223208
224- if ( state . name === "failed" )
225- return [
226- {
227- name : "revalidating-after-failure" ,
228- input : state . input ,
229- outcome : state . outcome ,
230- cachedOutcome : state . cachedOutcome ,
231- pending,
232- } ,
233- effects ,
234- ] ;
209+ const input = action . input || state . input ;
210+ const [ effects , pending ] = createFetchEffects < F > ( input , state . pending ) ;
211+
212+ if ( state . name === "succeeded" )
213+ return [
214+ {
215+ name : "revalidating-after-success" ,
216+ input : state . input ,
217+ outcome : state . outcome ,
218+ cachedOutcome : state . cachedOutcome ,
219+ pending,
220+ } ,
221+ effects ,
222+ ] ;
223+
224+ if ( state . name === "failed" )
225+ return [
226+ {
227+ name : "revalidating-after-failure" ,
228+ input : state . input ,
229+ outcome : state . outcome ,
230+ cachedOutcome : state . cachedOutcome ,
231+ pending,
232+ } ,
233+ effects ,
234+ ] ;
235+
236+ if ( state . name === "evaluating" )
237+ return [
238+ {
239+ name : "evaluating" ,
240+ input : state . input ,
241+ outcome : state . outcome ,
242+ cachedOutcome : state . cachedOutcome ,
243+ pending,
244+ } ,
245+ effects ,
246+ ] ;
235247
236- if ( state . name === "evaluating" )
248+ return tuple ;
249+ }
250+ case "settle/failure" : {
251+ if ( ! canSettle ( state ) ) return tuple ;
252+
253+ // ignore outdated responses
254+ if ( state . pending ?. id !== action . id ) return tuple ;
255+
256+ if ( action . thrownError ) {
257+ console . error ( "@happykit/flags: Failed to load flags" ) ;
258+ console . error ( action . thrownError ) ;
259+ }
260+
261+ const cachedOutcome = cache . get < SuccessOutcome < F > > ( action . input ) ;
237262 return [
238263 {
239- name : "evaluating" ,
240- input : state . input ,
241- outcome : state . outcome ,
242- cachedOutcome : state . cachedOutcome ,
243- pending,
264+ name : "failed" ,
265+ input : action . input ,
266+ outcome : action . outcome ,
267+ cachedOutcome,
244268 } ,
245- effects ,
269+ [ ] ,
246270 ] ;
247-
248- return tuple ;
249- }
250- case "settle/failure" : {
251- if ( ! canSettle ( state ) ) return tuple ;
252-
253- // ignore outdated responses
254- if ( state . pending ?. id !== action . id ) return tuple ;
255-
256- if ( action . thrownError ) {
257- console . error ( "@happykit/flags: Failed to load flags" ) ;
258- console . error ( action . thrownError ) ;
259271 }
272+ case "settle/success" : {
273+ if ( ! canSettle ( state ) ) return tuple ;
260274
261- const cachedOutcome = cache . get < SuccessOutcome < F > > ( action . input ) ;
262- return [
263- {
264- name : "failed" ,
265- input : action . input ,
266- outcome : action . outcome ,
267- cachedOutcome,
268- } ,
269- [ ] ,
270- ] ;
271- }
272- case "settle/success" : {
273- if ( ! canSettle ( state ) ) return tuple ;
275+ // ignore outdated responses
276+ if ( state . pending ?. id !== action . id ) return tuple ;
274277
275- // ignore outdated responses
276- if ( state . pending ?. id !== action . id ) return tuple ;
278+ const visitorKey = action . outcome . data . visitor ?. key ;
279+ if ( visitorKey ) {
280+ const visitorKeyCookie = config . serializeVisitorKeyCookie ( visitorKey ) ;
281+ if ( visitorKeyCookie ) document . cookie = visitorKeyCookie ;
282+ }
277283
278- const visitorKey = action . outcome . data . visitor ?. key ;
279- if ( visitorKey ) document . cookie = serializeVisitorKeyCookie ( visitorKey ) ;
284+ cache . set ( action . input , action . outcome ) ;
280285
281- cache . set ( action . input , action . outcome ) ;
286+ return [
287+ {
288+ name : "succeeded" ,
289+ input : action . input ,
290+ outcome : action . outcome ,
291+ } ,
292+ [ ] ,
293+ ] ;
294+ }
282295
283- return [
284- {
285- name : "succeeded" ,
286- input : action . input ,
287- outcome : action . outcome ,
288- } ,
289- [ ] ,
290- ] ;
296+ default :
297+ return tuple ;
291298 }
292-
293- default :
294- return tuple ;
295- }
299+ } ;
296300}
297301
298302function getInput < F extends Flags > ( {
@@ -409,6 +413,7 @@ export function createUseFlags<F extends Flags>(
409413 } : FactoryUseFlagOptions = { }
410414) {
411415 const config = applyConfigurationDefaults ( configuration ) ;
416+ const reducer = createReducer ( config ) ;
412417
413418 return function useFlags ( options : UseFlagsOptions < F > = { } ) : FlagBag < F > {
414419 useOnce ( ) ;
0 commit comments