Loading external js script in a React app - javascript

I have a React app created with create-react-app. Somebody gave me an external script to load, with three common jquery libraries. So I've searched a lot on the web to understand how to do it, but somehow, it doesnt' work.
My script can be found into the src/assets/js folder. And i use a tsconfig.json to make my base import url to "src" and I'm using react-router.
Method #1 - react-load-script - not working
I tried to load script with this library from NPM
function App() {
return (
<React.Fragment>
<Script
url="https://code.jquery.com/jquery-1.11.0.min.js"
/>
<Script url="assets/js/myscript.js" />
<SwitchRouter />
<React.Fragment>
);
}
Method #2 - homemade method with useEffect - not working
I tried to load script in a useEffect
function App() {
const appendScript = scriptToAppend => {
const script = document.createElement("script");
script.src = scriptToAppend;
script.async = true;
document.body.appendChild(script);
};
appendScript("assets/js/myscript.js")
appendScript("https://code.jquery.com/jquery-1.11.0.min.js")
}, []);
return (
<React.Fragment>
<SwitchRouter />
<React.Fragment>
);
}
Method #3 - In the index html - partially working
I tried to add directly the script in the public folder, and import it in the index.html, it work on the first load, but whenether you switch pages, the script isn't working anymore
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
<script src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="./myscript.js"></script>
</html>
So i'm kind of stuck because of this problem, have you an idea to explain why it is not working here ?

Related

Next Js fails to load third party script in _document.js [duplicate]

I try to understand how the next.js Script tag with the strategy beforeInteractive works. For testing i just used lodash. But i keep getting a ReferenceError: _ is not defined. I thought when a script is loaded with beforeInteractive it should be globally available inside my page Component since it get injected into the initial Html from the server and i could use it for example in the useEffect hook to alter a div.
Can someone explain to me why it's not working or what i'm doing wrong?
I don't installed it via npm because im trying to figure out how it works.
I have a simple _document.js and i added a Next.js script tag with the strategy beforeInteractive to this _document.js. The next.js docs says:
This strategy only works inside _document.js and is designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<Script
src="https://unpkg.com/lodash#4.17.20"
strategy="beforeInteractive"
></Script>
</body>
</Html>
)
}
Then i have a simple page Component inside the pages folder. I added the getServerSideProps function to use ServerSideRendering.
If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.
import Head from 'next/head';
import {useEffect, useState} from 'react';
const TestComponent = () => {
const [change,setChange] = useState('not changed');
useEffect(()=> {
console.log(_);
setChange(_.join(['one','two'],' - '));
});
return (
<>
<Head>
<title>Test</title>
</Head>
<div>{change}</div>
</>
);
};
export async function getServerSideProps(context) {
return {
props: {},
}
}
export default TestComponent;
Update
Seems like it is indeed a bug which is fixed but not released yet
https://github.com/vercel/next.js/discussions/37098
Putting aside the fact that you should be importing Lodash as a node module, there does seem to be an issue when using next/script in _document (no matter what the external script actually is).
It turns out this is a Next.js bug that has been addressed in this PR in pre-release version v12.1.7-canary.8. To fix the issue in your project simply update Next.js to version >=12.2.0 (npm install next#latest).
As an alternative, you can use the <script> tag directly in the _document's <Head> with the defer property. This closely matches what the next/script would output.
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head>
<script
type="text/javascript"
src="https://unpkg.com/lodash#4.17.20/lodash.js"
defer
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
First and foremost, I'm failing to see virtually any reason you'd want to do this, when you can (and should) simply use install it to node_modules. You're also going to possibly run the risk of the bundle having issues if the library type isn't a module and the next configuration requires a module.
Solution based on the question:
There's two ways.
Firstly, see the docs on this exact thing.
Please use the above method mentioned in the docs.
If that's not an option for whatever reason...
The second is a less than ideal, but working solution.
Create a folder for your static files. Ex: <root>/static/js/hello.js. Then in your _document file,
<script type="text/javascript" src="/static/hello.js"></script>

External script not loading on page change

I am trying to add a Trustpilot widget to my Gatsby.js website. It is required to load an external script from Trustpilot CDN.
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
I have tried multiple ways to add this script to my component. The first thing I tried was React Helmet. I added using the following code:
<Helmet>
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async></script>
The script seems to load when I initially load a page. Once I navigate to a different page, the styling goes away. As I reload, it comes back.
I tried adding the script inside componentDidMount()
componentDidMount() {
var addScript = document.createElement('script');
addScript.setAttribute('src', '//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js');
document.body.appendChild(addScript);
}
If you want your script (or any other component) to be persistent through your site, you need to use wrapPageElement or wrapRootElement APIs. Both APIs are suggested to be placed in gatsby-browser.js as well as in gatsby-ssr.js
Disclaimer: componentDidMount() will be triggered every time the DOM tree is loaded, it won't work for your use-case.
The issue here is that you are adding a non-React asset, not a component. You can try:
export const wrapPageElement = ({ element, props }) => {
return <SomeWrapper {...props}>{element}</SomeWrapper>;
};
Then, create a component called SomeWrapper and place your <Helmet>:
const SomeWrapper = (props) =>{
return <div>
<Helmet>
<script type="text/javascript" src="//widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js" async />
</Helmet>
{props.children}
</div>
}

Append multiple script tags to head that execute synchronously in React

I'm trying to add multiple script tags to the head of a page in React using react-helmet. I want these scripts to execute in the order they're included, as one would normally expect in regular HTML. But it seems there is a known bug in react-helmet which makes the scripts load asynchronously and they get executed in a random order which messes up the intended use since some scripts are dependent on other ones.
Is there a way to append multiple script tags to the <head> of a page in react so they execute in order?
Here's my code:
<Helmet>
{isPermissionAllowed && (
<script type="text/javascript" src={getFirstEndpoint()}></script>
)}
{isPermissionAllowed && (
<script type="text/javascript">{`window.sample = "${getSample()}";`}</script>
)}
{isPermissionAllowed && (
<script type="text/javascript" src={getSecondEndpoint()}></script>
)}
{isPermissionAllowed && (
<script type="text/javascript">window.example.text["app.sample"] = "sample";</script>
)}
</Helmet>
You could try this way inside your app.jsx:
const script = document.createElement("script");
script.src = 'Script source';
// script.[add attributes requires to you]
document.head.appendChild(script);

How to include external js files in an react component

I am trying to insert some external js files in my react component. I tried to include them in a simple html file in script tags and it was working fine but how to do in react component.
How I included them in html page:-
<script src="js/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/eclipse");
editor.session.setMode("ace/mode/c_cpp");
</script>
<body>
<div id="editor">
//text to display
</div>
</body>
id="editor" is defined in js files which is included in the script tags so main problem is how to include this in a react component. I have seen some results showing to use customReactHooks but no idea how to implement it . Any idea
You can make use of npm library named react-script-tag
const loadScript = (callback) => {
const script = document.createElement('script');
script.src = 'path'; // path for your script
script.id = 'some id';
document.body.appendChild(script);
script.onload = () => {
if (callback) callback();
};
}
call this function in the useEffect or componentDidMount of your react component

Next.js Loads <script> tags but it doesn't execute them

I'm integrating an existing React app into Next.js for mainly SEO features.
i pasted the css files links inside the <Header> tag in Next.js and they seem to work just fine. when i tried to do the same with the javascript files using the <script> tag, it doesn't execute the code inside those scripts. but i can see them loaded with http 200 in the chrome dev tools.
I tried using a library called Loadjs from npm to load the scripts inside componentDidMount but i got the exact same result as using <script> tag.
is there a proper way to do such simple thing in Next.js that i'm not aware of ?
Here's the code included in the pages/index.js file.
import React from "react"
import Head from 'next/head'
import MyAwesomeComponent from "../components/mycomponent.js"
export default () => (
<div>
<Head>
<link type="text/css" rel="stylesheet" href="static/css/chatwidget.css" />
<link type="text/css" rel="stylesheet" href="static/css/download.css" />
<script type="text/javascript" src="static/libs/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/malihu-custom-scrollbar-plugin#3.1.5/jquery.mCustomScrollbar.concat.min.js"></script>
<script type="text/javascript" src="static/libs/bootstrap.min.js"></script>
<script type="text/javascript" src="static/libs/owl.carousel.min.js"></script>
<script type="text/javascript" src="static/scripts/chatHead.js"></script>
<script type="text/javascript" src="static/libs/jquery.magnific-popup.js"></script>
</Head>
<MyAwesomeComponent /> {/* a simple React component that returns : <p>Hello World </p>*/}
</div>
)
Sorry for the late answer.
it turned out that all the scripts i linked missed one script that would actually run the functions for each action.
This works to me:
Create a folder for your static files:
<root>/public/static/script.js
in your index.js at <root>/pages/index.js
import Head from 'next/head';
import MyAwesomeComponent from '../components/mycomponent';
export default () => (
<div>
<Head>
<script type="text/javascript" src="/static/script.js"></script>
</Head>
<MyAwesomeComponent />
</div>
)
Note that static is the name I used in this example, it's not a requirement, it would work with any other folder name.
With the below approach you can easily put a script file's raw script text into a generated Next.js HTML page's <head> without screwing around with character escaping, formatting and general pretending that we aren't actually just building an HTML page in the end anyways.
There are many use cases you may want a script to run without going to network. Ex: 3rd party scripts, monitoring / analytics scripts that expect to be in the <head> without a separate network load. Many of these come minified, mangled, you-name-it and are just supposed to be copy, paste, move on with life.
Next.js makes this very hard pretending that everything with web development is magically React and Webpack all the time now (I wish right?)
The best developer experience way I've found is to do this:
_document.js
...
<Head>
<script type="text/javascript" dangerouslySetInnerHTML={{ __html: process.env.rawJsFromFile }}></script>
</Head>
...
next.config.js
https://github.com/vercel/next.js/blob/canary/packages/next/next-server/server/config.ts#L33
module.exports = {
env: {
rawJsFromFile: fs.readFileSync('./rawJsFromFile.js').toString()
}
}
rawJsFromFile.js
alert('Freedom!!!!!!!!!!!!!!!');
// and other 3rd party script junk, heck even minified JS is fine too if you need
Hope this saves someone from hours of frustration that I endured coming up with this... 😭
You can also run js code this
<script
dangerouslySetInnerHTML={{
__html: `
let a = 1;
functionCall();
`,
}}
></script>
With Next.js v11 and onward, you can use the Next component Script
https://nextjs.org/blog/next-11#script-optimization
<Script
src="..."
strategy="beforeInteractive"
/>
May this helps you Nextjs public folder
Move your static folder into public folder in your root directory
export default () => (
<div>
<Head>
<link type="text/css" rel="stylesheet" href="/static/css/chatwidget.css" />
<link type="text/css" rel="stylesheet" href="/static/css/download.css" />
<script type="text/javascript" src="/static/libs/jquery.min.js"></script>
...
</Head>
<MyAwesomeComponent />
</div>
)
This is what I tried and it worked for me.
I used two files entry-script.js and main-script.js. I put these like this
<root>/static/entry-script.js and <root>/static/main-script.js
The content of entry-script.js is below.
(function (d, t) {
t = d.createElement("script");
t.setAttribute("src", "/static/main-script.js");
d.getElementsByTagName("head")[0].appendChild(t);
})(document);
and the main logic is in the file main-script.js.
In the file _doucment.js of NextJS I included my file entry-script.js in body like below
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<script
type="text/javascript"
src="/static/entry-script.js"
></script>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
MyDocument.getInitialProps = async (ctx) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
I wrote an article elaborating on this question, hopefully it comes in handy:
https://www.devtwins.com/blog/how-to-add-a-third-party-script-to-a-nextjs-website
Or if you want to try another way to import Js file like I did
import { useEffect } from "react";
function MyApp({ Component, pageProps }) {
useEffect(() => {
import("../styles/bootstrap.bundle.min.js");
}, []);
return <></>
};

Categories