You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary
This PR adds a React Hook called `useFeature`, enabling feature usage (with variables) via the hooks interface.
The API designed here is intended to be feature complete with the `<OptimizelyFeature>` component.
### Justification - Why add hooks?
Despite just being the new hotness in the React world, there are a couple good reasons to supporting the hooks paradigm in this SDK:
- Easy use of features/variables outside the context of JSX markup
- Allow consumers to avoid using class components (mostly in the interest of bundle size)
- This could also be done by rewriting `<OptimizelyFeature>` as a functional component that itself leverages built in React hooks for state management
#### TODO
- [x] Update hook to return a third argument, `clientReady`, which will indicate whether or not the client is ready.
- [x] Add more tests for usage of the options and overrides
### Example Usage
_Directly from the README update:_
*arguments*
* `feature : string` Key of the feature
* `options : Object`
* `autoUpdate : boolean` (optional) If true, this component will re-render in response to datafile or user changes. Default: `false`.
* `timeout : number` (optional) Rendering timeout as described in the `OptimizelyProvider` section. Overrides any timeout set on the ancestor `OptimizelyProvider`.
* `overrides : Object`
* `overrideUserId : string` (optional) Override the userId for calls to `isFeatureEnabled` for this hook.
* `overrideAttributes : optimizely.UserAttributes` (optional) Override the user attributes for calls to `isFeatureEnabled` for this hook.
```jsx
import { useEffect } from 'react';
import { useFeature } from '@optimizely/react-sdk';
function LoginComponent() {
const [isEnabled, variables] = useFeature('feature1', { autoUpdate: true }, { /* (Optional) User overrides */ });
useEffect(() => {
document.title = isEnabled ? 'login1' : 'login2';
}, [isEnabled]);
return (
<p>
<a href={isEnabled ? "/login" : "/login2"}>
{variables.loginText}
</a>
</p>
)
}
```
### AutoUpdate
A utility was added to help manage auto updates of the feature and it's variables in `autoUpdate.ts`. Eventually, this utility can be used in a `useExperiment` hook.
Additionally, the `useFeature` and `useExperiment` hooks could then handle all the state management and auto update logic for `<OptimizelyFeature>` and `<OptimizelyExperiment>`, allowing those to just be thin wrapper components.
## Test Plan
Minimal unit tests have been added to test the base case(s) here. Additional testing of the optional parameters will be added once this overall approach is agreed upon.
_Note: Until we upgrade to React 16.9, there will be errors in the `hooks.spec.tsx` unit tests about not wrapping state setting test actions in `act()`. React 16.9 introduces async support for `act()`, which will allow us to wrap our invocation of `await client.onReady()`_
## Issues
Addresses #6
Copy file name to clipboardExpand all lines: CHANGELOG.md
+11Lines changed: 11 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
8
8
## [Unreleased]
9
9
10
+
### New Features
11
+
12
+
- Added `useFeature` hook
13
+
- Can be used to retrieve the status of a feature flag and its variables. See [#28](https://github.com/optimizely/react-sdk/pull/28) for more details.
14
+
15
+
### Enhancements
16
+
17
+
- Exposed the entire context object used by
18
+
- Enables support for using APIs which require passing reference to a context object, like `useContext`. [#27](https://github.com/optimizely/react-sdk/pull/27) for more details.
@@ -301,6 +301,53 @@ function FeatureComponent() {
301
301
}
302
302
```
303
303
304
+
305
+
## `useFeature` Hook
306
+
307
+
A [React Hook](https://reactjs.org/docs/hooks-intro.html) to retrieve the status of a feature flag and its variables. This can be useful as an alternative to the `<OptimizelyFeature>` component or to use features & variables inside code that is not explicitly rendered.
308
+
309
+
*arguments*
310
+
*`feature : string` Key of the feature
311
+
*`options : Object`
312
+
*`autoUpdate : boolean` (optional) If true, this hook will update the feature and it's variables in response to datafile or user changes. Default: `false`.
313
+
*`timeout : number` (optional) Client timeout as described in the `OptimizelyProvider` section. Overrides any timeout set on the ancestor `OptimizelyProvider`.
314
+
*`overrides : Object`
315
+
*`overrideUserId : string` (optional) Override the userId for calls to `isFeatureEnabled` for this hook.
316
+
*`overrideAttributes : optimizely.UserAttributes` (optional) Override the user attributes for calls to `isFeatureEnabled` for this hook.
317
+
318
+
*returns*
319
+
320
+
*`Array` of:
321
+
*`isFeatureEnabled : boolean` - The `isFeatureEnabled` value for the `feature` provided.
322
+
*`variables : VariableValuesObject` - The variable values for the `feature` provided
323
+
*`clientReady : boolean` - Whether or not the underlying `ReactSDKClient` instance is ready or not.
324
+
*`didTimeout : boolean` - Whether or not the underlying `ReactSDKClient` became ready within the allowed `timeout` range.
325
+
326
+
_Note: `clientReady` can be true even if `didTimeout` is also true. This indicates that the client became ready *after* the timeout period._
Any component under the `<OptimizelyProvider>` can access the Optimizely `ReactSDKClient` via the higher-order component (HoC) `withOptimizely`.
@@ -377,7 +424,7 @@ The following type definitions are used in the `ReactSDKClient` interface:
377
424
378
425
`ReactSDKClient` instances have the methods/properties listed below. Note that in general, the API largely matches that of the core `@optimizely/optimizely-sdk` client instance, which is documented on the [Optimizely X Full Stack developer docs site](https://docs.developers.optimizely.com/full-stack/docs). The major exception is that, for most methods, user id & attributes are ***optional*** arguments. `ReactSDKClient` has a current user. This user's id & attributes are automatically applied to all method calls, and overrides can be provided as arguments to these method calls if desired.
379
426
380
-
*`onReady(opts?: { timeout?: number }): Promise` Returns a Promise that fulfills with an object representing the initialization process. The instance is ready when it has fetched a datafile and a user is available (via `setUser` being called with an object, or a Promise passed to `setUser` becoming fulfilled).
427
+
*`onReady(opts?: { timeout?: number }): Promise<onReadyResult>` Returns a Promise that fulfills with an `onReadyResult`object representing the initialization process. The instance is ready when it has fetched a datafile and a user is available (via `setUser` being called with an object, or a Promise passed to `setUser` becoming fulfilled). If the `timeout` period happens before the client instance is ready, the `onReadyResult` object will contain an additional key, `dataReadyPromise`, which can be used to determine when, if ever, the instance does become ready.
381
428
*`user: User` The current user associated with this client instance
382
429
*`setUser(userInfo: User | Promise<User>): void` Call this to update the current user
383
430
*`onUserUpdate(handler: (userInfo: User) => void): () => void` Subscribe a callback to be called when this instance's current user changes. Returns a function that will unsubscribe the callback.
@@ -406,7 +453,7 @@ Right now server side rendering is possible with a few caveats.
406
453
407
454
1. You must download the datafile manually and pass in via the `datafile` option. Can not use `sdkKey` to automatically download.
408
455
409
-
2. Rendering of components must be completely synchronous (this is true for all server side rendering)
456
+
2. Rendering of components must be completely synchronous (this is true for all server side rendering), thus the Optimizely SDK assumes that the optimizely client has been instantiated and fired it's `onReady` event already.
0 commit comments