|
14 | 14 | * limitations under the License.
|
15 | 15 | */
|
16 | 16 | import * as React from 'react';
|
17 |
| -import { withOptimizely, WithOptimizelyProps } from './withOptimizely'; |
18 |
| -import { VariableValuesObject, OnReadyResult, DEFAULT_ON_READY_TIMEOUT } from './client'; |
19 |
| -import { getLogger } from '@optimizely/js-sdk-logging'; |
| 17 | +import { UserAttributes } from '@optimizely/optimizely-sdk'; |
20 | 18 |
|
21 |
| -const logger = getLogger('<OptimizelyFeature>'); |
| 19 | +import { VariableValuesObject } from './client'; |
| 20 | +import { useFeature } from './hooks'; |
| 21 | +import { withOptimizely, WithOptimizelyProps } from './withOptimizely'; |
22 | 22 |
|
23 | 23 | export interface FeatureProps extends WithOptimizelyProps {
|
24 |
| - // TODO add support for overrideUserId |
25 | 24 | feature: string;
|
26 | 25 | timeout?: number;
|
27 | 26 | autoUpdate?: boolean;
|
| 27 | + overrideUserId?: string; |
| 28 | + overrideAttributes?: UserAttributes; |
28 | 29 | children: (isEnabled: boolean, variables: VariableValuesObject) => React.ReactNode;
|
29 | 30 | }
|
30 | 31 |
|
31 |
| -export interface FeatureState { |
32 |
| - canRender: boolean; |
33 |
| - isEnabled: boolean; |
34 |
| - variables: VariableValuesObject; |
35 |
| -} |
36 |
| - |
37 |
| -class FeatureComponent extends React.Component<FeatureProps, FeatureState> { |
38 |
| - private optimizelyNotificationId?: number; |
39 |
| - private unregisterUserListener: () => void; |
40 |
| - private autoUpdate = false; |
41 |
| - |
42 |
| - constructor(props: FeatureProps) { |
43 |
| - super(props); |
44 |
| - |
45 |
| - this.unregisterUserListener = () => {}; |
46 |
| - |
47 |
| - const { autoUpdate, isServerSide, optimizely, feature } = props; |
48 |
| - this.autoUpdate = !!autoUpdate; |
49 |
| - if (isServerSide) { |
50 |
| - if (optimizely === null) { |
51 |
| - throw new Error('optimizely prop must be supplied'); |
52 |
| - } |
53 |
| - const isEnabled = optimizely.isFeatureEnabled(feature); |
54 |
| - const variables = optimizely.getFeatureVariables(feature); |
55 |
| - this.state = { |
56 |
| - canRender: true, |
57 |
| - isEnabled, |
58 |
| - variables, |
59 |
| - }; |
60 |
| - } else { |
61 |
| - this.state = { |
62 |
| - canRender: false, |
63 |
| - isEnabled: false, |
64 |
| - variables: {}, |
65 |
| - }; |
66 |
| - } |
67 |
| - } |
68 |
| - |
69 |
| - componentDidMount() { |
70 |
| - const { feature, optimizely, optimizelyReadyTimeout, isServerSide, timeout } = this.props; |
71 |
| - if (!optimizely) { |
72 |
| - throw new Error('optimizely prop must be supplied'); |
73 |
| - } |
74 |
| - |
75 |
| - if (isServerSide) { |
76 |
| - return; |
77 |
| - } |
78 |
| - |
79 |
| - // allow overriding of the ready timeout via the `timeout` prop passed to <Experiment /> |
80 |
| - const finalReadyTimeout: number | undefined = timeout !== undefined ? timeout : optimizelyReadyTimeout; |
81 |
| - |
82 |
| - optimizely.onReady({ timeout: finalReadyTimeout }).then((res: OnReadyResult) => { |
83 |
| - if (res.success) { |
84 |
| - logger.info('feature="%s" successfully rendered for user="%s"', feature, optimizely.user.id); |
85 |
| - } else { |
86 |
| - logger.info( |
87 |
| - 'feature="%s" could not be checked before timeout of %sms, reason="%s" ', |
88 |
| - feature, |
89 |
| - timeout === undefined ? DEFAULT_ON_READY_TIMEOUT : timeout, |
90 |
| - res.reason || '' |
91 |
| - ); |
92 |
| - } |
93 |
| - |
94 |
| - const isEnabled = optimizely.isFeatureEnabled(feature); |
95 |
| - const variables = optimizely.getFeatureVariables(feature); |
96 |
| - this.setState({ |
97 |
| - canRender: true, |
98 |
| - isEnabled, |
99 |
| - variables, |
100 |
| - }); |
101 |
| - |
102 |
| - if (this.autoUpdate) { |
103 |
| - this.setupAutoUpdateListeners(); |
104 |
| - } |
105 |
| - }); |
106 |
| - } |
107 |
| - |
108 |
| - setupAutoUpdateListeners() { |
109 |
| - const { optimizely, feature } = this.props; |
110 |
| - if (optimizely === null) { |
111 |
| - return; |
112 |
| - } |
113 |
| - |
114 |
| - this.optimizelyNotificationId = optimizely.notificationCenter.addNotificationListener( |
115 |
| - 'OPTIMIZELY_CONFIG_UPDATE', |
116 |
| - () => { |
117 |
| - logger.info('OPTIMIZELY_CONFIG_UPDATE, re-evaluating feature="%s" for user="%s"', feature, optimizely.user.id); |
118 |
| - const isEnabled = optimizely.isFeatureEnabled(feature); |
119 |
| - const variables = optimizely.getFeatureVariables(feature); |
120 |
| - this.setState({ |
121 |
| - isEnabled, |
122 |
| - variables, |
123 |
| - }); |
124 |
| - } |
125 |
| - ); |
126 |
| - |
127 |
| - this.unregisterUserListener = optimizely.onUserUpdate(() => { |
128 |
| - logger.info('User update, re-evaluating feature="%s" for user="%s"', feature, optimizely.user.id); |
129 |
| - const isEnabled = optimizely.isFeatureEnabled(feature); |
130 |
| - const variables = optimizely.getFeatureVariables(feature); |
131 |
| - this.setState({ |
132 |
| - isEnabled, |
133 |
| - variables, |
134 |
| - }); |
135 |
| - }); |
| 32 | +const FeatureComponent = (props: FeatureProps): any => { |
| 33 | + const { feature, timeout, autoUpdate, children, overrideUserId, overrideAttributes } = props; |
| 34 | + const [isEnabled, variables, clientReady, didTimeout] = useFeature( |
| 35 | + feature, |
| 36 | + { timeout, autoUpdate }, |
| 37 | + { overrideUserId, overrideAttributes } |
| 38 | + ); |
| 39 | + |
| 40 | + if (!clientReady && !didTimeout) { |
| 41 | + // Only block rendering while were waiting for the client within the allowed timeout. |
| 42 | + return null; |
136 | 43 | }
|
137 | 44 |
|
138 |
| - componentWillUnmount() { |
139 |
| - const { optimizely, isServerSide } = this.props; |
140 |
| - if (isServerSide || !this.autoUpdate) { |
141 |
| - return; |
142 |
| - } |
143 |
| - if (optimizely && this.optimizelyNotificationId) { |
144 |
| - optimizely.notificationCenter.removeNotificationListener(this.optimizelyNotificationId); |
145 |
| - } |
146 |
| - this.unregisterUserListener(); |
147 |
| - } |
148 |
| - |
149 |
| - render() { |
150 |
| - const { children } = this.props; |
151 |
| - const { isEnabled, variables, canRender } = this.state; |
152 |
| - |
153 |
| - if (!canRender) { |
154 |
| - return null; |
155 |
| - } |
156 |
| - |
157 |
| - return children(isEnabled, variables); |
158 |
| - } |
159 |
| -} |
| 45 | + return children(isEnabled, variables); |
| 46 | +}; |
160 | 47 |
|
161 | 48 | export const OptimizelyFeature = withOptimizely(FeatureComponent);
|
0 commit comments