Skip to content

Commit

Permalink
chore: Add useVariation and useVariationDetail hooks for react univer…
Browse files Browse the repository at this point in the history
…sal (#501)
  • Loading branch information
yusinto authored Jul 12, 2024
1 parent 467a086 commit 9eac9db
Show file tree
Hide file tree
Showing 16 changed files with 129 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
'use client';

import { useLDClient } from '@launchdarkly/react-universal-sdk/client';
import { useVariationDetail } from '@launchdarkly/react-universal-sdk/client';

export default function HelloClientComponent() {
const ldc = useLDClient();

// WARNING: Using the ldClient to evaluate flags directly like this in prod
// can result in high event volumes. This example is contrived and is meant for
// demo purposes only. The recommended way is to utilise the `useVariation` hooks
// which should be supported soon.
const flagValue = ldc.variation('my-boolean-flag-1');
// You need to set evaluationReasons to true when initializing the LDProvider to useVariationDetail.
// Note: in the future evaluationReasons will be renamed withReasons.
const detail = useVariationDetail('my-boolean-flag-1');

return (
<div className="border-2 border-white/20 p-4 ">
<div>
<p className="ldgradient text-xl">
{flagValue
{detail.value
? 'This flag is evaluating True running Client-Side JavaScript'
: 'This flag is evaluating False running Client-Side JavaScript'}
</p>
<p>Reason: {detail.reason?.kind ?? 'reason is null'}</p>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@ import { useState } from 'react';
import { useCookies } from 'react-cookie';

import type { JSSdk } from '@launchdarkly/react-universal-sdk';
import { useLDClient } from '@launchdarkly/react-universal-sdk/client';
import { useLDClient, useVariation } from '@launchdarkly/react-universal-sdk/client';

export default function HelloIdentify() {
const ldc = useLDClient();
const [_, setCookie] = useCookies(['ld']);
const [contextKey, setContextKey] = useState('');

// WARNING: Using the ldClient to evaluate flags directly like this in prod
// can result in high event volumes. This example is contrived and is meant for
// demo purposes only. The recommended way is to utilise the `useVariation` hooks
// which should be supported soon.
const flagValue = ldc.variation('my-boolean-flag-1');
const ldc = useLDClient();
const flagValue = useVariation('my-boolean-flag-1');

function onClickLogin() {
const context = { kind: 'user', key: contextKey };
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { getLDContext } from '@/app/utils';

import { useLDClientRsc } from '@launchdarkly/react-universal-sdk/server';
import { useVariationRsc } from '@launchdarkly/react-universal-sdk/server';

export default async function HelloServerComponent() {
const ldc = await useLDClientRsc(getLDContext());
const flagValue = ldc.variation('my-boolean-flag-1');
const flagValue = await useVariationRsc('my-boolean-flag-1', getLDContext());

return (
<div className="border-2 border-white/20 p-4">
Expand Down
6 changes: 5 additions & 1 deletion packages/sdk/react-universal/example/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ export default async function RootLayout({
return (
<html lang="en">
<body className={inter.className}>
<LDProvider clientSideID={clientSideID} context={context} options={{ bootstrap }}>
<LDProvider
clientSideID={clientSideID}
context={context}
options={{ bootstrap, evaluationReasons: true }}
>
{children}
</LDProvider>
</body>
Expand Down
1 change: 1 addition & 0 deletions packages/sdk/react-universal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"typescript": "5.1.6"
},
"dependencies": {
"@launchdarkly/js-client-sdk-common": "^1.1.4",
"@launchdarkly/node-server-sdk": "^9.4.6",
"launchdarkly-js-client-sdk": "^3.4.0"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/react-universal/src/client/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// TODO: Implement variation and typed variation hooks.
export * from './variation';

export * from './useLDClient';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useVariation';
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { LDEvaluationDetailTyped } from '@launchdarkly/js-client-sdk-common';

import { useLDClient } from '../useLDClient';

export const useTypedVariation = <T extends boolean | number | string | unknown>(
key: string,
defaultValue: T,
): T => {
const ldClient = useLDClient();

switch (typeof defaultValue) {
// case 'boolean':
// return ldClient.boolVariation(key, defaultValue as boolean) as T;
// case 'number':
// return ldClient.numberVariation(key, defaultValue as number) as T;
// case 'string':
// return ldClient.stringVariation(key, defaultValue as string) as T;
// case 'undefined':
// case 'object':
// return ldClient.jsonVariation(key, defaultValue) as T;
default:
return ldClient.variation(key, defaultValue);
}
};

export const useTypedVariationDetail = <T extends boolean | number | string | unknown>(
key: string,
defaultValue: T,
): LDEvaluationDetailTyped<T> => {
const ldClient = useLDClient();

switch (typeof defaultValue) {
// case 'boolean':
// return ldClient.boolVariationDetail(
// key,
// defaultValue as boolean,
// ) as LDEvaluationDetailTyped<T>;
// case 'number':
// return ldClient.numberVariationDetail(
// key,
// defaultValue as number,
// ) as LDEvaluationDetailTyped<T>;
// case 'string':
// return ldClient.stringVariationDetail(
// key,
// defaultValue as string,
// ) as LDEvaluationDetailTyped<T>;
// case 'undefined':
// case 'object':
// return ldClient.jsonVariationDetail(key, defaultValue) as LDEvaluationDetailTyped<T>;
default:
return ldClient.variationDetail(key, defaultValue) as LDEvaluationDetailTyped<T>;
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useTypedVariation, useTypedVariationDetail } from './useTypedVariation';

export const useVariation = (key: string, defaultValue?: boolean) =>
useTypedVariation<any>(key, defaultValue);

/**
* Note that this will only work if you have set `withReasons` to true in {@link LDOptions}.
* Otherwise, the `reason` property of the result will be null.
*
* @param key
* @param defaultValue
*/
export const useVariationDetail = (key: string, defaultValue?: boolean) =>
useTypedVariationDetail<any>(key, defaultValue);
23 changes: 21 additions & 2 deletions packages/sdk/react-universal/src/ldClientRsc.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import type { LDContext, LDFlagSet, LDFlagValue } from '@launchdarkly/node-server-sdk';
import type {
LDContext,
LDEvaluationDetail,
LDFlagSet,
LDFlagValue,
} from '@launchdarkly/node-server-sdk';

import { isServer } from './isServer';
import type { JSSdk } from './types';

// GOTCHA: Partially implement the js sdk.
// Omit variationDetail because its return type is incompatible with js-core.
type PartialJSSdk = Omit<Partial<JSSdk>, 'variationDetail'>;

/**
* A partial ldClient suitable for RSC and server side rendering.
*/
export class LDClientRsc implements Partial<JSSdk> {
export class LDClientRsc implements PartialJSSdk {
constructor(
private readonly ldContext: LDContext,
private readonly bootstrap: LDFlagSet,
Expand All @@ -27,4 +36,14 @@ export class LDClientRsc implements Partial<JSSdk> {
}
return this.bootstrap[key] ?? defaultValue;
}

variationDetail(key: string, defaultValue?: LDFlagValue): LDEvaluationDetail {
if (isServer) {
// On the server during ssr, call variation for analytics purposes.
global.nodeSdk.variationDetail(key, this.ldContext, defaultValue).then(/* ignore */);
}

const { reason, variation: variationIndex } = this.bootstrap.$flagsState[key];
return { value: this.bootstrap[key], reason, variationIndex };
}
}
2 changes: 1 addition & 1 deletion packages/sdk/react-universal/src/server/getBootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import type { LDContext } from '@launchdarkly/node-server-sdk';
* @returns A promise which resolves to a json object suitable for bootstrapping the js sdk.
*/
export const getBootstrap = async (context: LDContext) => {
const allFlags = await global.nodeSdk.allFlagsState(context);
const allFlags = await global.nodeSdk.allFlagsState(context, { withReasons: true });
return allFlags?.toJSON();
};
3 changes: 3 additions & 0 deletions packages/sdk/react-universal/src/server/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './variation';

export * from './useLDClientRsc';
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { cache } from 'react';

import type { LDContext } from '@launchdarkly/node-server-sdk';

import { LDClientRsc } from '../ldClientRsc';
import { getBootstrap } from './getBootstrap';
import { LDClientRsc } from '../../ldClientRsc';
import { getBootstrap } from '../getBootstrap';

const ldClientRsc = 'ldClientRsc';
const getServerCache = cache(() => new Map<string, any>());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useVariationRsc';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { LDContext, LDFlagValue } from '@launchdarkly/node-server-sdk';

import { useLDClientRsc } from '../useLDClientRsc';

export const useVariationRsc = async (key: string, context: LDContext, def?: LDFlagValue) => {
const ldc = await useLDClientRsc(context);
return ldc.variation(key, def);
};

export const useVariationDetailRsc = async (key: string, context: LDContext, def?: LDFlagValue) => {
const ldc = await useLDClientRsc(context);
return ldc.variationDetail(key, def);
};
2 changes: 1 addition & 1 deletion packages/sdk/react-universal/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './initNodeSdk';
export * from './useLDClientRsc';
export * from './hooks';
export * from './getBootstrap';

0 comments on commit 9eac9db

Please sign in to comment.