|
1 | 1 | # JS SDK Wrapper
|
2 | 2 |
|
3 |
| -# What is it |
| 3 | +### What is it |
4 | 4 |
|
5 | 5 | - A backwards compatible wrapper around the JavascriptSDK
|
6 | 6 | - Provides extendible datafile loading and caching strategies
|
7 |
| -- Handles async loading of userId and UserAttributes |
8 |
| -- Provides mechanisms to block rendering / execution until Optimizely is loaeded with a fallback timeout |
| 7 | +- Provides mechanisms to block rendering / execution until Optimizely is loaded with the ability to set a maximum timeout duration |
9 | 8 | - All new features are opt-in, can be used exactly the same way as JavascriptSDK if desired
|
| 9 | +- Enqueue `track` calls that happen before the datafile is downloaded |
10 | 10 |
|
11 | 11 |
|
12 |
| -# Datafile loading / management |
| 12 | +## Datafile loading / management |
13 | 13 |
|
14 |
| -## Load datafile already on the page |
| 14 | +### Load datafile already on the page |
15 | 15 |
|
16 | 16 | This is the ideal case and prevents a lot of timing issues and complexity, however we realize not all customers will have the ability to this.
|
17 | 17 |
|
18 | 18 | ```js
|
19 |
| -import { Optimizely } from '@optimizely/js-web-sdk' |
20 |
| -const optimizely = new Optimizely({ |
| 19 | +import * as optimizelySDK from '@optimizely/js-web-sdk' |
| 20 | +const optimizely = optimizelySDK.createInstance({ |
21 | 21 | datafile: window.datafile,
|
22 | 22 | })
|
23 | 23 | // all calls can happen immediately after (sync)
|
24 | 24 | optimizely.activate('my-exp', 'user1')
|
25 | 25 | ```
|
26 | 26 |
|
27 |
| -## Load datafile by URL |
| 27 | +### Load datafile by SDK Key |
28 | 28 |
|
29 |
| -This is not an optimal solution as it requires us to think about timing and ensure that we only call `optimizely` functions after the datafile is loaded or ensure we handle the case where `optimizely` is not ready and we need to delay loading or display a default. |
| 29 | +By providing the `sdkKey` option to `createInstance` the SDK will automatically fetch the datafile. If a cached datafile exists it will use the cached version. Decisions made after the fresh datafile has loaded will use the new datafile. |
30 | 30 |
|
31 | 31 | _Asnyc load and wait until datafile is loaded_
|
32 | 32 |
|
33 | 33 | ```js
|
34 |
| -import { Optimizely } from '@optimizely/js-web-sdk' |
35 |
| - |
36 |
| -const optimizely = new Optimizely({ |
| 34 | +import * as optimizelySDK from '@optimizely/js-web-sdk' |
| 35 | +const optimizely = optimizelySDK.createInstance({ |
37 | 36 | SDKKey: 'GaXr9RoDhRcqXJm3ruskRa',
|
38 | 37 | })
|
39 |
| -await optimizely.onReady() |
40 |
| -// datafile is gauranteed to be loaded |
| 38 | + |
| 39 | +// At this point optimizely can be used, on first page load the datafile will not be fetched and methods will no-op |
| 40 | +// On second page load it will use the cached datafile immediately |
| 41 | +// |
41 | 42 | initApp()
|
42 | 43 | ```
|
43 | 44 |
|
44 |
| -The above example may not be great, perhaps you want a gaurantee that the page wont block longer than X milliseconds. |
| 45 | +#### `optimizely.onReady()` to block rendering |
45 | 46 |
|
46 |
| -_Asnyc load and wait up til 100ms_ |
| 47 | +By using `await optimizely.onReady()` you can gaurantee code wont be run until the datafile is downloaded |
47 | 48 |
|
48 | 49 | ```js
|
49 |
| -import { Optimizely } from '@optimizely/js-web-sdk' |
50 |
| - |
51 |
| -const optimizely = new Optimizely({ |
| 50 | +import * as optimizelySDK from '@optimizely/js-web-sdk' |
| 51 | +const optimizely = optimizelySDK.createInstance({ |
52 | 52 | SDKKey: 'GaXr9RoDhRcqXJm3ruskRa',
|
53 | 53 | })
|
54 |
| -// dont block for more than 100sec |
55 |
| -await optimizely.onReady({ timeout: 100 }) |
56 |
| -// at this point datafile may or may not be loaded |
57 |
| -// however calls to track will be queued when the datafile is ready |
58 |
| -initApp() |
59 | 54 |
|
60 |
| -// additionally in other places you can hook into onReady without a timeout |
61 |
| -// to gaurantee optimizely is loaded |
62 |
| -optimizely.onReady().then(() => { |
63 |
| - // optimizely is gauranteed to be loaded at this point |
64 |
| -}) |
| 55 | +await optimizely.onReady() |
| 56 | +// at this point datafile is gauranteed to be loaded |
| 57 | +initApp() |
65 | 58 | ```
|
66 | 59 |
|
67 |
| -### Second page load |
68 |
| - |
69 |
| -By default loading the datafile by URL will store the contents of the datafile in `localStorage`, on second page load we are guaranteed to have synchronous access to the datafile. |
70 |
| - |
71 |
| -The underlying DatafileManager will also make a background request to get an updated datafile, however that will not be registered until the next instantiation of `Optimizely` which is usually the next page load. |
72 |
| - |
73 |
| -_When using optimizely async the user will only have to pay the loading cost once on first page load, subsequent page loads are always synchronous_ |
74 |
| - |
75 |
| -### Using React |
| 60 | +However, the above example isn't great because Optimizely could time out due to network connectivity issues. By passing a `timeout` to `optimizely.onReady()` we can gaurantee that Optimizely won't block the page for more than X milliseconds. |
76 | 61 |
|
77 | 62 | ```js
|
78 |
| -// ./optimizely.js |
79 |
| -import optimizelySDK from '@optimizely/js-web-sdk' |
80 |
| - |
| 63 | +import * as optimizelySDK from '@optimizely/js-web-sdk' |
81 | 64 | const optimizely = optimizelySDK.createInstance({
|
82 | 65 | SDKKey: 'GaXr9RoDhRcqXJm3ruskRa',
|
83 |
| - userId: window.userId, |
84 | 66 | })
|
85 | 67 |
|
86 |
| -export { optimizely } |
87 |
| -``` |
88 |
| - |
89 |
| -```jsx |
90 |
| -// ./App.jsx |
91 |
| -import React, { Component } from 'react' |
92 |
| -import { optimizely } from './optimizely' |
93 |
| -import { |
94 |
| - OptimizelyProvider, |
95 |
| - OptimizelyExperiment, |
96 |
| -} from '@optimizely/react-sdk' |
97 |
| - |
98 |
| -class App extends Component { |
99 |
| - render() { |
100 |
| - <OptimizelyProvider optimizely={optimizely} timeout={50}> |
101 |
| - <OptimizelyExperiment experiment="header-test"> |
102 |
| - {variation => |
103 |
| - variation === 'detailed' ? <DetailedHeader /> : <SimpleHeader /> |
104 |
| - } |
105 |
| - </OptimizelyExperiment> |
106 |
| - </OptimizelyProvider> |
107 |
| - } |
108 |
| -} |
109 |
| -``` |
| 68 | +// Dont wait more than 200ms, if there is a cached datafile this will immediately resolve |
| 69 | +await optimizely.onReady({ timeout: 200 }) |
110 | 70 |
|
111 |
| -In the above example, setting `timeout={50}` will allow any Optimizely components to wait up to 50ms for the datafile to load. |
112 | 71 |
|
113 |
| -_Benefits to the React approach_ |
114 |
| -In the case where the datafile is already loaded, either from being on the page already or cached in local storage this approach doesn’t have a flash or a loading spinner. |
115 |
| - |
116 |
| -On first page load, if the datafile is slow (due to slow connection) it will render the fallback. |
117 |
| - |
118 |
| -# User management |
119 |
| - |
120 |
| -## Storing userId and attributes on instance |
121 |
| - |
122 |
| -This SDK supports remembering userId and attributes by passing them to instantiation |
123 |
| - |
124 |
| -```js |
125 |
| -import { Optimizely } from '@optimizely/js-web-sdk' |
126 |
| -const optimizely = new Optimizely({ |
127 |
| - datafile: window.datafile, |
128 |
| - userId: window.userId |
129 |
| - attibutes: { |
130 |
| - plan_type: 'silver' |
131 |
| - } |
132 |
| -}) |
133 |
| -// no need to pass userId or attributes |
134 |
| -optimizely.activate('my-exp') |
135 |
| - |
136 |
| -// you can always override on a per call basis |
137 |
| -optimizely.activate('my-exp', 'otheruser', { |
138 |
| - plan_type: 'gold', |
| 72 | +// you can also use the Promise API |
| 73 | +optimizely.onReady({ timeout: 200 }).then(() => { |
| 74 | + initApp() |
139 | 75 | })
|
140 |
| - |
141 |
| -// However this isn't recommeneded as "track" calls also need to match this |
142 |
| -// TODO: does easy event tracking fix this? |
143 | 76 | ```
|
144 | 77 |
|
145 |
| -## Generating a random user Id and storing in cookie |
| 78 | +It's worth noting that `optimizely.onReady` can be called as many times, once the datafile has downloaded this will always return a resolved promise. This is a powerful mechanism to build UI components, as a UI component can be configured to block up to X milliseconds waiting for Optimizely to load, while other parts of the UI are unaffected. |
146 | 79 |
|
147 |
| -The following code will generate a random userId if the user doesnt already have one saved in a cookie. |
148 | 80 |
|
149 |
| -```js |
150 |
| -import { |
151 |
| - Optimizely, |
152 |
| - CookieRandomUserIdLoader, |
153 |
| -} from '@optimizely/js-web-sdk' |
| 81 | +### Second page load |
154 | 82 |
|
155 |
| -const optimizely = new Optimizely({ |
156 |
| - datafile: window.datafile, |
157 |
| - userIdLoader: new CookieRandomUserIdLoader(), |
158 |
| - attibutes: { |
159 |
| - plan_type: 'silver' |
160 |
| - } |
161 |
| -}) |
162 |
| -``` |
| 83 | +By default loading the datafile by URL will store the contents of the datafile in `localStorage`, on second page load we are guaranteed to have synchronous access to the datafile. |
163 | 84 |
|
164 |
| -## Not managing User IDs |
| 85 | +The underlying DatafileManager will also make a background request to get an updated datafile, however that will not be registered until the next instantiation of Optimizely via `optimizely.createInstance` which is usually the next page load. |
165 | 86 |
|
166 |
| -Of course this is totally opt in, you can continue to pass userId into all api calls, the same as the Node Javascript SDK |
| 87 | +_When using optimizely async the user will only have to pay the loading cost once on first page load, subsequent page loads are always synchronous_ |
167 | 88 |
|
168 |
| -```js |
169 |
| -import { Optimizely } from '@optimizely/js-web-sdk' |
| 89 | +## Using React |
170 | 90 |
|
171 |
| -const optimizely = new Optimizely({ |
172 |
| - datafile: window.datafile, |
173 |
| -}) |
174 |
| -optimizely.activate('exp1', 'user1') |
175 |
| -``` |
| 91 | +This SDK can be used stand alone to bolster the current javascript-sdk with things like automatic datafile loading and caching. It can also be used with the [ReactSDK](../react-sdk) to simplify Feature Flagging and AB Testing in React. |
0 commit comments