Skip to content

Commit 2cd2f3f

Browse files
dobesvhudovisk
authored andcommitted
feat: update loading logic and fix ssr
- Add 3 second timeout, show default variant if Google Optimize fails to load (e.g. due to ad blocking) - Add check for `typeof window !== 'undefined'`, show default variant for SSR - Use `window.google_optimize` to check if script is already loaded - Don't rely on `dataLayer.hide.end` being present to decide whether to wait
1 parent 342f1d4 commit 2cd2f3f

File tree

2 files changed

+51
-32
lines changed

2 files changed

+51
-32
lines changed

src/Experiment.js

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,84 @@ import OptimizeContext from "./OptimizeContext";
44

55
class Experiment extends React.Component {
66
static defaultProps = {
7-
loader: null
7+
loader: null,
8+
timeout: 3000
89
};
910

1011
static propTypes = {
1112
id: PropTypes.string.isRequired,
1213
loader: PropTypes.node,
14+
timeout: PropTypes.number,
1315
children: PropTypes.node
1416
};
1517

1618
state = {
1719
variant: null
1820
};
1921

22+
updateVariantTimeout = null;
23+
2024
updateVariant = value => {
25+
clearTimeout(this.updateVariantTimeout);
2126
// if experiment not active, render original
22-
this.setState({
23-
variant: value === undefined || value === null ? "0" : value
24-
});
27+
const newVariant = value === undefined || value === null ? "0" : value;
28+
if (newVariant !== this.state.variant) {
29+
this.setState({
30+
variant: newVariant
31+
});
32+
}
2533
};
2634

27-
delayedInitialization = () => {
35+
updateVariantFromGlobalState = () => {
2836
const value =
29-
window.google_optimize && window.google_optimize.get(this.props.id);
37+
typeof window !== "undefined" && window.google_optimize
38+
? window.google_optimize.get(this.props.id)
39+
: null;
3040
this.updateVariant(value);
3141
};
3242

43+
setupOptimizeCallback = () => {
44+
this.updateVariantTimeout = setTimeout(
45+
this.updateVariantFromGlobalState,
46+
this.props.timeout
47+
);
48+
const oldHideEnd = window.dataLayer.hide.end;
49+
window.dataLayer.hide.end = () => {
50+
this.updateVariantFromGlobalState();
51+
oldHideEnd && oldHideEnd();
52+
};
53+
54+
window.gtag &&
55+
window.gtag("event", "optimize.callback", {
56+
name: this.props.id,
57+
callback: this.updateVariant
58+
});
59+
};
60+
3361
componentDidMount() {
3462
if (!this.props.id) {
3563
throw new Error("Please specify the experiment id");
3664
}
3765

3866
// Delayed init
39-
const hideEnd =
40-
window.dataLayer && window.dataLayer.hide && window.dataLayer.hide.end;
41-
if (hideEnd) {
42-
window.dataLayer.hide.end = () => {
43-
this.delayedInitialization();
44-
hideEnd();
45-
};
67+
if (typeof window !== "undefined" && !window.google_optimize) {
68+
if (!window.dataLayer) {
69+
window.dataLayer = [];
70+
}
71+
if (!window.dataLayer.hide) {
72+
window.dataLayer.hide = { start: Date.now() };
73+
}
74+
75+
this.setupOptimizeCallback();
4676
} else {
47-
this.delayedInitialization();
77+
// Google Optimize already loaded, or we're doing server-side rendering
78+
this.updateVariantFromGlobalState();
4879
}
49-
50-
window.gtag &&
51-
window.gtag("event", "optimize.callback", {
52-
name: this.props.id,
53-
callback: this.updateVariant
54-
});
5580
}
5681

5782
componentWillUnmount() {
58-
window.gtag &&
83+
typeof window !== "undefined" &&
84+
window.gtag &&
5985
window.gtag("event", "optimize.callback", {
6086
name: this.props.id,
6187
callback: this.updateVariant,

test/specs/experiment.spec.js

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ describe("experiment", () => {
1010

1111
describe("on optimize already loaded", () => {
1212
it("should render original variant on experiment not active", () => {
13+
window.google_optimize = { get: sinon.stub().returns(null) };
14+
1315
const wrapper = shallow(<Experiment id="abc" />);
1416

1517
expect(wrapper.state("variant")).to.be.equal("0");
@@ -27,18 +29,9 @@ describe("experiment", () => {
2729
});
2830
});
2931

30-
describe("on optmize loading", () => {
31-
beforeEach(() => {
32-
window.dataLayer = {
33-
hide: { end: () => {} }
34-
};
35-
});
36-
37-
afterEach(() => {
38-
delete window.dataLayer;
39-
});
40-
32+
describe("on optimize not loaded yet", () => {
4133
it("should render loader", () => {
34+
delete window.dataLayer;
4235
const Loader = () => <div>loader</div>;
4336
const wrapper = shallow(<Experiment id="abc" loader={<Loader />} />);
4437

0 commit comments

Comments
 (0)