Unable to Inject 3rd Party Scripts in Gatsby - reactjs

The following code is required to be injected into the head for Sovrn display ads:
<script type="application/javascript">var googletag=googletag||{};googletag.cmd=googletag.cmd||[];googletag.cmd.push(function(){googletag.pubads().disableInitialLoad()});</script><script type="application/javascript" src="//ap.lijit.com/www/headerauction/headersuite.min.js?configId=XXXX"></script>
It looks for a div with ID="21728840" to display an ad for that div. I'm using Netlify so injecting this script into the head is not an issue, but Netlify does this post-process. Therefore, by the time, the script loads, it is not able to find the ID since the element is not defined, and returns a 403 error. I looked at many suggestions, and tried to use useEffect in the file where the ad is like so:
export const BlogIndexTemplate = ({ posts, title...
...
useEffect(() => {
var googletag = googletag || {}
googletag.cmd = googletag.cmd || []
googletag.cmd.push(function() {
googletag.pubads().disableInitialLoad()
})
const script = document.createElement('script')
script.async = true
script.src =
'//ap.lijit.com/www/headerauction/headersuite.min.js?configId=XXXX'
document.head.appendChild(script)
}, [])
...
{!!posts.length && (
<section className="section">
<div className="container">
<PostSection posts={filteredPosts} />
</div>
</section>
)}
The ad container is located in PostSection like so:
class PostSection extends React.Component {
...
<div className="sidebar-sticky-container">
<div className="ad-skyscraper-container sticky-widget">
<div id="21728840"></div>
I'm not sure what other approach I could try to get the script injected, and manipulate the div with ID 21728840 to display the ad.

After a lot of research, I discovered that using useEffect and most of the suggestions online are unnecessary for Gatsby. To inject a 3rd party script, one needs to create a gatsby-ssr.js file in the root directory. Inside this file, one should have the following for a basic set up:
const React = require('react')
exports.onRenderBody = function({
setHeadComponents,
setPreBodyComponents
}) {
setHeadComponents([
<script
dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])
setPreBodyComponents([
<script
dangerouslySetInnerHTML={{whateveryouneedtoset}}>
])
}
All options for using the gatsby-ssr.js file can be found here: Gatsby SSR

Related

Not able to connect AdSense to Gatsby blog

I've been trying to connect my AdSense account with my Gatsby blog and it seems impossible. AdSense is asking me to place this code between the head tag of my html
<script data-ad-client="ca-pub-XXXXXXXXXXXXXXXX" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
I've tried gatsby adsense plugins and other things and AdSense keeps telling me the code is not in the website. Since the website is hosted in S3, I downloaded the generated index.html and changed the code and re uploaded it. I think the problem is due to an added attribute called data-checked-head to the script tag, so even though I add the code above, what I see in the browser is this:
<script data-ad-client="ca-pub-XXXXXXXXXXXXXXXX" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js" data-checked-head="true"></script>
If this code is what AdSense sees, then of course he doesn't recognize it. Does anyone know what can I do in this case?? Or why is this attribute even there?? Thanks
I can't answer about the details of AdSense but I have had problems with meta tags in the head of HTML myself. Here's two possibilites to debug your code in regards to Gatsby:
Many plugins are disabled by default in development mode. Try gatsby build and gatsby serve and then check if it works with plugins.
Use react-helmet to place your script tag in the head of HTML. Use gatsby build and gatsby serve for testing this as well.
You can use gatsby-plugin-google-adsense for displaying ads on your site.
The best way I found is from this article, which suggest a simple React implementation of Google AdSense.
In your gatsby-ssr.js file:
const React = require('react')
const HeadComponents = [
<script
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXX"
crossOrigin="anonymous"
async
/>,
]
exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => {
setHeadComponents(HeadComponents)
}
Then you create a Banner component to include in your Gatsby.js pages:
const Banner: React.FC<BannerProps> = ({
className,
style,
layout,
format,
client = 'ca-pub-XXXX',
slot,
responsive,
layoutKey,
}) => {
useEffect(() => {
try {
const adsbygoogle = window.adsbygoogle || []
adsbygoogle.push({})
} catch (e) {
console.error(e)
}
}, [])
return (
<div className="banner-container">
<ins
className="adsbygoogle"
style={style}
data-ad-layout={layout}
data-ad-format={format}
data-ad-client={client}
data-ad-slot={slot}
data-ad-layout-key={layoutKey}
data-full-width-responsive={responsive}
/>
</div>
)
}
Full article here.

How can I ignore TypeScript errors when adding script tags via react-helmet?

The following tsx code generates TypeScript errors. //#ts-ignore doesn't work.
<Helmet>
<script>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-*******');
</script>
</Helmet>
The following code works, but I would like to use a child <script> tag vs the script component property.
<Helmet
script={[
{
type: 'application/javascript',
innerHTML: '(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\'gtm.start\':new Date().getTime(),event:\'gtm.js\'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=\'dataLayer\'?\'&l=\'+l:\'\';j.async=true;j.src=\'https://www.googletagmanager.com/gtm.js?id=\'+i+dl;f.parentNode.insertBefore(j,f);})(window,document,\'script\',\'dataLayer\',\'GTM-*******\');'
}
]}
/>
Figured it out thanks to https://github.com/nfl/react-helmet/issues/334#issuecomment-413319383.
<Helmet>
<script>
{`
alert('BOOM');
`}
</script>
</Helmet>
Does this script have to be embedded as part of your Component heap?
I faced a similar issue when using YouTube's iframe API and the solution was to create the script element and reference the javascript objects created once they exist (if you need to do that, looks like you literally just want to import some JS). I didn't need to create a react component for this unless there were visual elements I wanted to manage.
// GoogleTagManager.ts
export class GoogleTagManager {
someVariable: someVariableType = null
constructor () {
const script: HTMLScriptElement = <HTMLScriptElement>document.createElement('script')
script.src = '[URL_TO_JS_FILE]'
script.onload = (): void => console.log('loaded google tag manager')
document.body.appendChild(script)
}
WaitForVariable (): void {
let timeout: number
const OnTimeout = (): void => {
if (timeout) {
clearTimeout(timeout)
}
if (typeof [TARGET_VARIABLE] !== 'undefined') {
this.someVariable = [TARGET_VARIABLE]
return
}
timeout = setTimeout(OnTimeout, 1000)
}
}
}
If you want to display some information then you could turn this into a React Component and move the constructor logic to componentDidMount. Alternatively I would consider sending any resultant data to a store and having a separate component for rendering the resultant data.
With caution, you can use dangerouslySetInnerHTML like so:
<script
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-*******');
`,
}}
/>
But be careful of cross-site scripting (XSS) attacks.

How to show build datetime on my react web app using create-react-app?

tl;dr version - I want to show the date of deployment of the production application on the footer of my application so that the users know exactly when the application was last updated.
I want it to be automatic, but I can't think of a way to capture the current date time during build and then parse it into html (jsx) to show them.
Update - Btw, I use CRA and have no plans of ejecting from it! At least not for this.
Context - Why do I want this?
I have a production application in React that I don't have unrestricted access to. There were some issues raised on there and I was able to replicate it on my dev and I fixed the issues and deployed it (or I thought I did). But the issue remained on production and after more time debugging, I just asked for a lot of diagnostic information from them to check if there was something wrong with the production API or database etc. But it finally turned out that my deployment to production wasn't successful. I think that if I showed the last deployement date-time somewhere on the app, I could just ask that from the users rather than get into elaborate diagnostic information.
I'm a Create React App maintainer!
Starting in Create React App 2 (react-scripts#^2.0) you can accomplish this via macros.
First, install preval.macro:
$ npm install --save preval.macro # if using npm
$ yarn add preval.macro # if using yarn
Next, in the file you want to render a build timestamp in, include preval.macro:
import preval from 'preval.macro'
Finally, you can create a constant and use it in your app like so:
const dateTimeStamp = preval`module.exports = new Date().toLocaleString();`
Here's a full example:
import React, { Component } from 'react'
import logo from './logo.svg'
import './App.css'
import preval from 'preval.macro'
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Build Date: {preval`module.exports = new Date().toLocaleString();`}.
</p>
</header>
</div>
)
}
}
export default App
The solution from joe-haddad is working well. Here is a drop-in component you can use like <VersionNumber />. This will display something like:
import React from 'react';
import moment from 'moment';
import packageJson from '../../../package.json';
import preval from 'preval.macro';
const buildTimestamp = preval`module.exports = new Date().getTime();`;
class Component extends React.Component {
getDateString = () => {
const lastUpdateMoment = moment.unix(buildTimestamp / 1000);
const formattedDate = lastUpdateMoment.format('DD.MM.YYYY HH:mm:ss');
return formattedDate;
};
render () {
return (
<div>
{packageJson.version}
{'.'}
{buildTimestamp}
{' '}
{'('}
{this.getDateString()}
{')'}
</div>
);
}
}
Component.propTypes = {};
Component.defaultProps = {};
export default Component;
Set a process environment using webpack. You can save the value to a local json file and read from it with an increment during the compilation.
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce(
(env, key) => {
env[key] = process.env[key];
return env;
},
{
// Useful for determining whether we’re running in production mode.
// Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development',
REACT_APP_DATE_TIME: new Date().toDateString(),
// Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put
// images into the `src` and `import` them in code to get their paths.
PUBLIC_URL: publicUrl,
}
);
// Stringify all values so we can feed into Webpack DefinePlugin
const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]);
return env;
}, {}),
};
return { raw, stringified };
}
module.exports = getClientEnvironment;
The old solution will also work. Please check the code again. As i can see the proper value in my app.
A contribution to Joe Haddat's answer; if you want to illustrate the build date by using preval.macro in the viewer timezone instead of the environment timezone which builds the app, here is how to do it;
render() {
const buildDate = preval`module.exports = new Date();`
return (
<div>Build Date: {new Date(buildDate).toLocaleString()}</div>
)
}
This will save the build date, but turning it into a locale string will still happen on runtime, on the browser where the viewer timezone is used.

Reactjs old work is rendering to the browser, new code having no impact in browser

I was working on a React application that I had to not work on for awhile.
I came back to it. I start up my local server using live-server public/
and no matter what I do on my app.js file, nothing renders. I can still see the last work I did on the browser, but my app.js file is having no impact in the browser even though what is rendering on the browser is from my app.js file. Honestly, I can never get used to this silent failure on the part of React. All the files are loading properly.
What should I be looking for? What am I missing?
This is my app.js file:
console.log('App.js is running');
const app = {
title: 'Decision',
subtitle: 'For binary life decisions, put your trust in a computer',
options: []
};
const onFormSubmit = e => {
e.preventDefault();
const option = e.target.elements.option.value;
if (option) {
app.options.push(option);
e.target.elements.option.value = '';
render();
}
};
const onRemoveAll = () => {
app.options = [];
render();
};
// create "Remove All" button above list
// on click -> wipe the array -> rerender
const appRoot = document.getElementById('app');
const numbers = [55, 101, 1000];
const render = () => {
const template = (
<div>
<h1>{app.title}</h1>
{app.subtitle && <p>{app.subtitle}</p>}
<p>{app.options.length > 0 ? 'Here are your options' : 'No options'}</p>
<p>{app.options.length}</p>
<button onClick={onRemoveAll}>Remove All</button>
{
[99, 98, 97]
}
<ol>
<li>Item one</li>
<li>Item two</li>
</ol>
<form onSubmit={onFormSubmit}>
<input type="text" name="option" />
<button>Add Option</button>
</form>
</div>
);
ReactDOM.render(template, appRoot);
};
render();
The issue was your application was not transpiling, so you were not seeing any changes made in your code render in the browser. This also explains why you were seeing only what you last worked on.
You were using yarn for one thing and then using npm install for something else. If yarn installations are not working for you, you can start using npm, but once you do, stick with it.
Redo the package.json installation except do it with npm.
Run npm init
Reinstall with npm install babel-preset-react babel-preset-env live-server
You should be able to see all the changes you make to your app.js file render now.

How to pass Serverside parameter in ASP.Core to React?

I created a React/Typescript project with dotnet new "ASP.NET Core with React.js".
index.cshtml:
<div id="react-app"></div>
#section scripts {
<script src="~/dist/main.js" asp-append-version="true">
</script>
}
boot.tsx(shortened):
function renderApp() {
ReactDOM.render(
<AppContainer>
<BrowserRouter children={ routes } />
</AppContainer>,
document.getElementById('react-app')
);
}
renderApp();
if (module.hot) {
module.hot.accept('./routes', () => {
routes = require<typeof RoutesModule>('./routes').routes;
renderApp();
});
}
How can I pass ASP.Core generated information(the routes from the controllers) to my react/typescript code?
To use server-side rendering in your application, follow the following steps:
1 - Modify App_Start\ReactConfig.cs (for ASP.NET MVC 4 or 5) or Startup.cs (for ASP.NET Core) to reference your components:
namespace MyApp
{
public static class ReactConfig
{
public static void Configure()
{
ReactSiteConfiguration.Configuration = new ReactSiteConfiguration()
.AddScript("~/Scripts/HelloWorld.jsx");
}
}
}
This tells ReactJS.NET to load all the relevant JavaScript files server-side. The JavaScript files of all the components you want to load and all their dependencies should be included here.
2 - In your ASP.NET MVC view, call Html.React to render a component server-side, passing it the name of the component, and any required props.
#Html.React("HelloWorld", new
{
name = "Daniel"
})
3 - Call Html.ReactInitJavaScript at the bottom of the page (just above the ) to render initialisation scripts. Note that this does not load the JavaScript files for your components, it only renders the initialisation code.
<!-- Load all your scripts normally before calling ReactInitJavaScript -->
<!-- Assumes minification/combination is configured as per previous section -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>
#Scripts.Render("~/bundles/main")
#Html.ReactInitJavaScript()
4 - Hit the page and admire the server-rendered beauty:
<div id="react1">
<div data-reactid=".2aubxk2hwsu" data-react-checksum="-1025167618">
<span data-reactid=".2aubxk2hwsu.0">Hello </span>
<span data-reactid=".2aubxk2hwsu.1">Daniel</span>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.js"></script>
<script src="/Scripts/HelloWorld.js"></script>
<script>ReactDOM.render(HelloWorld({"name":"Daniel"}), document.getElementById("react1"));</script>
The server-rendered HTML will automatically be reused by React client-side, meaning your initial render will be super fast.
If you encounter any errors with the JavaScript, you may want to temporarily disable server-side rendering in order to debug your components in your browser. You can do this by calling DisableServerSideRendering() in your ReactJS.NET config.
For a more in-depth example, take a look at the included sample application (React.Samples.Mvc4).
5 - Server-side only rendering
If there is no need to have a React application client side and you just want to use the server side rendering but without the React specific data attributes, call Html.React and pass serverOnly parameter as true.
#Html.React("HelloWorld", new
{
name = "Daniel"
}, serverOnly: true)
And the HTML will look like the one following which is a lot cleaner. In this case there is no need to load the React script or call the Html.ReactInitJavaScript() method.
<div id="react1">
<div>
<span>Hello </span>
<span>Daniel</span>
</div>
</div>

Resources