Skip to content

Commit 5327e3c

Browse files
authored
Merge pull request #236 from Siggnja/implement-timeout-support
Adding optional prop for lazy load the intercom script
2 parents 5b39af0 + 3a7c6c4 commit 5327e3c

File tree

8 files changed

+108
-11
lines changed

8 files changed

+108
-11
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Place the `IntercomProvider` as high as possible in your application. This will
7575
| onUnreadCountChange | (number) => void | triggered when the current number of unread messages changes | false | |
7676
| shouldInitialize | boolean | indicates if the Intercom should be initialized. Can be used in multistaged environment | false | true |
7777
| apiBase | string | If you need to route your Messenger requests through a different endpoint than the default. Generally speaking, this is not needed.<br/> Format: `https://${INTERCOM_APP_ID}.intercom-messenger.com` (See: [https://github.com/devrnt/react-use-intercom/pull/96](https://github.com/devrnt/react-use-intercom/pull/96)) | false | |
78+
| initializeDelay | number | Indicates if the intercom initialization should be delayed, delay is in ms, defaults to 0. See https://github.com/devrnt/react-use-intercom/pull/236 | false | |
7879

7980
#### Example
8081
```javascript
@@ -239,6 +240,15 @@ These props are `JavaScript` 'friendly', so [camelCase](https://en.wikipedia.org
239240
> Mind that all the properties in `react-use-intercom` are camel cased, except for the `customAttributes` property in the `boot` and `update` method from `useIntercom`.
240241
241242
## Advanced
243+
244+
### Delay initialization
245+
246+
`<IntercomProvider />` uses an official intercom snippet and is directly initialized on load. In the background this snippet will load some external code that makes Intercom work. All of this magic happens on the initial load and in some use cases this can become problematic (E.g. when LCP is priority).
247+
248+
Since [v1.2.0](https://github.com/devrnt/react-use-intercom/releases/tag/v1.2.0) it's possible to delay this initialisation by passing `initializeDelay` in `<IntercomProvider />` (it's in milliseconds). However most of the users won't need to mess with this.
249+
250+
For reference see https://github.com/devrnt/react-use-intercom/pull/236 and https://forum.intercom.com/s/question/0D52G00004WxWLs/can-i-delay-loading-intercom-on-my-site-to-reduce-the-js-load
251+
### useCallback
242252
To reduce the amount of re-renders in your React application I suggest to make use of [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback)
243253

244254
**TLDR:** `useCallback` will return a memoized version of the callback that only changes if one of the dependencies has changed.

playground/app.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ProviderEventsPage,
99
ProviderApiPage,
1010
UseIntercomTourPage,
11+
UseIntercomWithDelay
1112
} from './modules';
1213

1314
import { Page, Style } from './modules/common';
@@ -47,6 +48,7 @@ const App = () => {
4748
<Route path="/providerApi" component={ProviderApiPage} />
4849
<Route path="/useIntercom" component={UseIntercomPage} />
4950
<Route path="/useIntercomTour" component={UseIntercomTourPage} />
51+
<Route path="/useIntercomWithTimeout" component={UseIntercomWithDelay} />
5052
<Route path="/" exact>
5153
<Navigation>
5254
<Link to="/provider">
@@ -61,6 +63,9 @@ const App = () => {
6163
<Link to="/useIntercomTour">
6264
<code>useIntercom with tour</code>
6365
</Link>
66+
<Link to="/useIntercomWithTimeout">
67+
<code>useIntercom with delayed boot</code>
68+
</Link>
6469
</Navigation>
6570
</Route>
6671
</Router>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { default as UseIntercomPage } from './useIntercom';
22
export { default as UseIntercomTourPage } from './useIntercomTour';
3+
export { default as UseIntercomWithDelay } from './useIntercomWithDelay'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import * as React from 'react';
2+
import styled from 'styled-components';
3+
4+
import { IntercomProvider } from '../../../.';
5+
6+
const Grid = styled.div`
7+
display: grid;
8+
grid-template-columns: repeat(1, 1fr);
9+
width: 100%;
10+
`;
11+
12+
const Item = styled.div`
13+
display: grid;
14+
grid-template-rows: min-content;
15+
16+
&::after {
17+
content: '';
18+
margin: 2rem 0 1.5rem;
19+
border-bottom: 2px solid var(--grey);
20+
width: 100%;
21+
}
22+
`;
23+
24+
const RawUseIntercomPage = () => {
25+
return (
26+
<Grid>
27+
<Item>
28+
<p>Intercom will be initialized (and autobooted) after 5000ms</p>
29+
</Item>
30+
</Grid>
31+
);
32+
};
33+
34+
const UseIntercomWithDelayPage = () => {
35+
return (
36+
<IntercomProvider appId="jcabc7e3" initializeDelay={5000} autoBoot>
37+
<RawUseIntercomPage />
38+
</IntercomProvider>
39+
);
40+
};
41+
42+
export default UseIntercomWithDelayPage;

src/initialize.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
* Snippet to initialize the Intercom instance
44
*
55
* @param appId - Intercom app id
6+
* @param [timeout=0] - Amount of milliseconds that the initialization should be delayed, defaults to 0
67
*
78
* @see {@link https://developers.intercom.com/installing-intercom/docs/basic-javascript}
89
*/
9-
const initialize = (appId: string) => {
10+
const initialize = (appId: string, timeout = 0) => {
1011
var w = window;
1112
var ic = w.Intercom;
1213
if (typeof ic === 'function') {
@@ -23,12 +24,14 @@ const initialize = (appId: string) => {
2324
};
2425
w.Intercom = i;
2526
var l = function() {
26-
var s = d.createElement('script');
27-
s.type = 'text/javascript';
28-
s.async = true;
29-
s.src = 'https://widget.intercom.io/widget/' + appId;
30-
var x = d.getElementsByTagName('script')[0];
31-
x.parentNode.insertBefore(s, x);
27+
setTimeout(function() {
28+
var s = d.createElement('script');
29+
s.type = 'text/javascript';
30+
s.async = true;
31+
s.src = 'https://widget.intercom.io/widget/' + appId;
32+
var x = d.getElementsByTagName('script')[0];
33+
x.parentNode.insertBefore(s, x);
34+
}, timeout);
3235
};
3336
if (document.readyState === 'complete') {
3437
l();

src/provider.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const IntercomProvider: React.FC<IntercomProviderProps> = ({
2222
onUnreadCountChange,
2323
shouldInitialize = !isSSR,
2424
apiBase,
25+
initializeDelay,
2526
...rest
2627
}) => {
2728
const isBooted = React.useRef(autoBoot);
@@ -36,7 +37,7 @@ export const IntercomProvider: React.FC<IntercomProviderProps> = ({
3637
);
3738

3839
if (!isSSR && !window.Intercom && shouldInitialize) {
39-
initialize(appId);
40+
initialize(appId, initializeDelay);
4041
// Only add listeners on initialization
4142
if (onHide) IntercomAPI('onHide', onHide);
4243
if (onShow) IntercomAPI('onShow', onShow);
@@ -56,7 +57,10 @@ export const IntercomProvider: React.FC<IntercomProviderProps> = ({
5657
}
5758

5859
const ensureIntercom = React.useCallback(
59-
(functionName: string = 'A function', callback: Function) => {
60+
(
61+
functionName: string = 'A function',
62+
callback: (() => void) | (() => string),
63+
) => {
6064
if (!window.Intercom && !shouldInitialize) {
6165
logger.log(
6266
'warn',
@@ -173,8 +177,8 @@ export const IntercomProvider: React.FC<IntercomProviderProps> = ({
173177

174178
const getVisitorId = React.useCallback(() => {
175179
return ensureIntercom('getVisitorId', () => {
176-
return (IntercomAPI('getVisitorId') as unknown) as string;
177-
});
180+
return IntercomAPI('getVisitorId');
181+
}) as string;
178182
}, [ensureIntercom]);
179183

180184
const startTour = React.useCallback(

src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,10 @@ export type IntercomProviderProps = {
419419
* Format https://${INTERCOM_APP_ID}.intercom-messenger.com
420420
*/
421421
apiBase?: string;
422+
/**
423+
* Indicates if the intercom initialization should be delayed, delay is in ms
424+
*
425+
* @remarks If not set delay is set to 0ms
426+
* */
427+
initializeDelay?: number;
422428
};

test/useIntercom.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,30 @@ describe('useIntercom', () => {
4444

4545
expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID });
4646
});
47+
48+
test('should await a certain amount on delayed initialization', async () => {
49+
const { result, waitFor } = renderHook(() => useIntercom(), {
50+
wrapper: ({ children }) => (
51+
<IntercomProvider appId={INTERCOM_APP_ID} initializeDelay={5000}>
52+
{children}
53+
</IntercomProvider>
54+
),
55+
});
56+
57+
const { boot } = result.current;
58+
59+
act(() => {
60+
boot();
61+
});
62+
63+
expect(window.intercomSettings).toEqual({ app_id: undefined });
64+
65+
await waitFor(() => {}, { timeout: 5000 });
66+
67+
act(() => {
68+
boot();
69+
});
70+
71+
expect(window.intercomSettings).toEqual({ app_id: INTERCOM_APP_ID });
72+
});
4773
});

0 commit comments

Comments
 (0)