Skip to content

Commit f69f52d

Browse files
pavkamroncohen
authored andcommitted
feat(react-sdk): add onFeaturesUpdated prop and useClient hook (#329)
1 parent 8c696cf commit f69f52d

4 files changed

Lines changed: 63 additions & 19 deletions

File tree

packages/react-sdk/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,27 @@ function FeatureOptIn() {
396396

397397
Note: To change the `user.id` or `company.id`, you need to update the props passed to `BucketProvider` instead of using these hooks.
398398

399+
### `useClient()`
400+
401+
Returns the `BucketClient` used by the `BucketProvider`. The client offers more functionality that
402+
is not directly accessible thorough the other hooks.
403+
404+
```tsx
405+
import { useClient } from "@bucketco/react-sdk";
406+
407+
function LoggingWrapper({ children }: { children: ReactNode }) {
408+
const client = useClient();
409+
410+
useEffect(() => {
411+
client.on("enabledCheck", (evt) => {
412+
console.log(`The feature ${evt.key} is ${evt.value} for user.`);
413+
});
414+
}, [client]);
415+
416+
return children;
417+
}
418+
```
419+
399420
## Content Security Policy (CSP)
400421

401422
See [CSP](https://github.com/bucketco/bucket-javascript-sdk/blob/main/packages/browser-sdk/README.md#content-security-policy-csp) for info on using Bucket React SDK with CSP

packages/react-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bucketco/react-sdk",
3-
"version": "3.0.0-alpha.6",
3+
"version": "3.0.0-alpha.7",
44
"license": "MIT",
55
"repository": {
66
"type": "git",

packages/react-sdk/src/index.tsx

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ type EmptyConfig = {
166166
payload: undefined;
167167
};
168168

169-
type Feature<TKey extends FeatureKey> = {
169+
export type Feature<TKey extends FeatureKey> = {
170170
isEnabled: boolean;
171171
isLoading: boolean;
172172
config: MaterializedFeatures[TKey] extends boolean
@@ -195,12 +195,10 @@ type Feature<TKey extends FeatureKey> = {
195195
export function useFeature<TKey extends FeatureKey>(
196196
key: TKey,
197197
): Feature<typeof key> {
198+
const client = useClient();
198199
const {
199200
features: { isLoading },
200-
client,
201-
provider,
202201
} = useContext<ProviderContextType>(ProviderContext);
203-
ensureProvider(provider);
204202

205203
const track = () => client?.track(key);
206204
const requestFeedback = (opts: RequestFeedbackOptions) =>
@@ -241,8 +239,7 @@ export function useFeature<TKey extends FeatureKey>(
241239
* ```
242240
*/
243241
export function useTrack() {
244-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
245-
ensureProvider(provider);
242+
const client = useClient();
246243
return (eventName: string, attributes?: Record<string, any> | null) =>
247244
client?.track(eventName, attributes);
248245
}
@@ -262,8 +259,7 @@ export function useTrack() {
262259
* ```
263260
*/
264261
export function useRequestFeedback() {
265-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
266-
ensureProvider(provider);
262+
const client = useClient();
267263
return (options: RequestFeedbackData) => client?.requestFeedback(options);
268264
}
269265

@@ -284,8 +280,7 @@ export function useRequestFeedback() {
284280
* ```
285281
*/
286282
export function useSendFeedback() {
287-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
288-
ensureProvider(provider);
283+
const client = useClient();
289284
return (opts: UnassignedFeedback) => client?.feedback(opts);
290285
}
291286

@@ -303,8 +298,7 @@ export function useSendFeedback() {
303298
* ```
304299
*/
305300
export function useUpdateUser() {
306-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
307-
ensureProvider(provider);
301+
const client = useClient();
308302
return (opts: { [key: string]: string | number | undefined }) =>
309303
client?.updateUser(opts);
310304
}
@@ -323,8 +317,7 @@ export function useUpdateUser() {
323317
* ```
324318
*/
325319
export function useUpdateCompany() {
326-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
327-
ensureProvider(provider);
320+
const client = useClient();
328321

329322
return (opts: { [key: string]: string | number | undefined }) =>
330323
client?.updateCompany(opts);
@@ -345,16 +338,30 @@ export function useUpdateCompany() {
345338
* ```
346339
*/
347340
export function useUpdateOtherContext() {
348-
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
349-
ensureProvider(provider);
341+
const client = useClient();
350342
return (opts: { [key: string]: string | number | undefined }) =>
351343
client?.updateOtherContext(opts);
352344
}
353345

354-
function ensureProvider(provider: boolean) {
346+
/**
347+
* Returns the current `BucketClient` used by the `BucketProvider`.
348+
*
349+
* This is useful if you need to access the `BucketClient` outside of the `BucketProvider`.
350+
*
351+
* ```ts
352+
* const client = useClient();
353+
* client.on("configCheck", () => {
354+
* console.log("configCheck hook called");
355+
* });
356+
* ```
357+
*/
358+
export function useClient() {
359+
const { client, provider } = useContext<ProviderContextType>(ProviderContext);
355360
if (!provider) {
356361
throw new Error(
357362
"BucketProvider is missing. Please ensure your component is wrapped with a BucketProvider.",
358363
);
359364
}
365+
366+
return client;
360367
}

packages/react-sdk/test/usage.test.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { version } from "../package.json";
1919
import {
2020
BucketProps,
2121
BucketProvider,
22+
useClient,
2223
useFeature,
2324
useRequestFeedback,
2425
useSendFeedback,
@@ -138,6 +139,7 @@ beforeAll(() =>
138139
},
139140
}),
140141
);
142+
141143
afterEach(() => server.resetHandlers());
142144
afterAll(() => server.close());
143145

@@ -311,7 +313,7 @@ describe("useSendFeedback", () => {
311313

312314
await waitFor(async () => {
313315
await result.current({
314-
featureId: "123",
316+
featureKey: "huddles",
315317
score: 5,
316318
});
317319
expect(events).toStrictEqual(["FEEDBACK"]);
@@ -432,3 +434,17 @@ describe("useUpdateOtherContext", () => {
432434
unmount();
433435
});
434436
});
437+
438+
describe("useClient", () => {
439+
test("gets the client", async () => {
440+
const { result: clientFn, unmount } = renderHook(() => useClient(), {
441+
wrapper: ({ children }) => getProvider({ children }),
442+
});
443+
444+
await waitFor(async () => {
445+
expect(clientFn.current).toBeDefined();
446+
});
447+
448+
unmount();
449+
});
450+
});

0 commit comments

Comments
 (0)