Skip to content

Commit

Permalink
react script cmp
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdbradley committed Sep 30, 2021
1 parent adfd0b6 commit 1cdf59a
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
```ts

// @public
export const appendForwardConfig: (propertyName: PartytownForwardPropertyName, propertyType?: PartytownForwardPropertyType) => string;
export const appendForwardProperty: (propertyName: PartytownForwardPropertyName, propertyType?: PartytownForwardPropertyType) => string;

// @public (undocumented)
export interface PartytownConfig {
Expand Down
2 changes: 1 addition & 1 deletion src/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const partytownSnippet = (config: PartytownConfig) => {
*
* @public
*/
export const appendForwardConfig = (
export const appendForwardProperty = (
propertyName: PartytownForwardPropertyName,
propertyType?: PartytownForwardPropertyType
) =>
Expand Down
2 changes: 1 addition & 1 deletion src/react/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Partytown, PartytownProps } from './partytown';
export { Partytown, PartytownProps } from './library';

export {
GoogleTagManager,
Expand Down
30 changes: 10 additions & 20 deletions src/react/integration/gtm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Fragment } from 'react';
import { googleTagManager } from '../../integration/services/gtm';
import { appendForwardConfig, SCRIPT_TYPE } from '@builder.io/partytown/intergration';
import { appendForwardProperty, SCRIPT_TYPE } from '@builder.io/partytown/intergration';
import { PartytownScript } from '../script';

/**
* https://developers.google.com/tag-manager/quickstart
Expand All @@ -25,25 +26,14 @@ export type GoogleTagManagerProps = {
*
* @public
*/
export const GoogleTagManager = ({ containerId }: GoogleTagManagerProps): any => (
<Fragment>
{/*
Purposely does not have type="text/partytown" attribute so that the
config is added to the main thread window
*/}
<script
dangerouslySetInnerHTML={{
__html: appendForwardConfig('dataLayer', 1),
}}
/>
<script
type={SCRIPT_TYPE}
dangerouslySetInnerHTML={{
__html: googleTagManager(containerId),
}}
/>
</Fragment>
);
export const GoogleTagManager = ({ containerId }: GoogleTagManagerProps): any => {
return (
<Fragment>
<PartytownScript id="gtm" innerHTML={appendForwardProperty('dataLayer', 1)} />
<PartytownScript id="gtm-pt" innerHTML={googleTagManager(containerId)} type={SCRIPT_TYPE} />
</Fragment>
);
};

/**
* The GTM No Script component should be added immediately after the opening `<body>` tag.
Expand Down
8 changes: 2 additions & 6 deletions src/react/partytown.tsx → src/react/library.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import type { PartytownConfig } from '../lib/types';
import { partytownSnippet } from '@builder.io/partytown/intergration';
import { PartytownScript } from './script';

/**
* Props for `<Partytown/>`, which extends the Partytown Config.
Expand All @@ -18,10 +19,5 @@ export interface PartytownProps extends PartytownConfig {}
* @public
*/
export const Partytown = (props?: PartytownProps): any => (
<script
data-partytown="lib"
dangerouslySetInnerHTML={{
__html: partytownSnippet({ ...props }),
}}
/>
<PartytownScript id="lib" innerHTML={partytownSnippet({ ...props })} />
);
52 changes: 52 additions & 0 deletions src/react/script.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useEffect } from 'react';
import { SCRIPT_TYPE } from '@builder.io/partytown/intergration';

export interface PartytownScriptProps {
/**
* Must be a unique id, and sets the `data-pt-script` attribute
* which is used as a selector to ensure the same script isn't executed
* more than once.
*/
id: string;
innerHTML: string;
type?: string;
}

/**
* React script component created for these scenarios:
* - SSR/Static rendered HTML, without Client JS running
* - Client JS only, without starting with rendered SSR/Static HTML
* - SSR/Static rendered HTML, that is then hydrated with Client JS
*
* This component ensures each scenario works, and that the script is only
* executed once (to include not re-executing during Hot Module Reloading).
* Also ensures it does nothing for scripts with [type="text/partytown"].
*/
export const PartytownScript = ({ id, innerHTML, type }: PartytownScriptProps): any => {
useEffect(() => {
if (typeof document !== 'undefined' && type !== SCRIPT_TYPE) {
if (!document.querySelector(`script[data-pt-script="${id}"]`)) {
// Client side, that's not a Partytown script, that should run on main
// SSR/Static script does not exist, this must be client only
// append the script script that should have run and append to the head
const scriptElm = document.createElement('script');
scriptElm.dataset.ptScript = id;
scriptElm.innerHTML = innerHTML;
if (type) {
scriptElm.setAttribute('type', type);
}
document.head.appendChild(scriptElm);
}
}
}, []);

// `dangerouslySetInnerHTML` only works for scripts rendered as HTML from SSR/Static.
// Added code will set the [type="data-pt-script"] attribute with the unique id.
// If this code renders as SSR/Static HTML, then it'll execute and add the attribute
// which will tell the Client JS of the component to NOT add the same script to the head.
let jsxInnerHTML = innerHTML;
if (type !== SCRIPT_TYPE) {
jsxInnerHTML = `document.currentScript.dataset.ptScript=${JSON.stringify(id)};` + jsxInnerHTML;
}
return <script dangerouslySetInnerHTML={{ __html: jsxInnerHTML }} type={type} />;
};

1 comment on commit 1cdf59a

@vercel
Copy link

@vercel vercel bot commented on 1cdf59a Sep 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.