513

I have a relatively straightforward issue of trying to add inline scripting to a React component. What I have so far:

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>
                    
                    <script src="https://use.typekit.net/foobar.js"></script>
                    <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}></script>
                </article>
            </DocumentTitle>
        );
    }
};

I have also tried:

<script src="https://use.typekit.net/foobar.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>

Neither approach seems to execute the desired script. I'm guessing it's a simple thing I'm missing. Can anybody help out?

PS: Ignore the foobar, I have a real id actually in use that I didn't feel like sharing.

Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
ArrayKnight
  • 6,956
  • 4
  • 17
  • 20
  • 8
    Is there specific motivation for loading this via React instead of including it in your base page HTML? Even if this did work, it would mean you would be re-inserting a script every time the component mounted. – loganfsmyth Dec 22 '15 at 21:54
  • Is that the case? I assumed DOM diffing would make that not the case, but I admit it would depend on the implementation of `DocumentTitle`. – loganfsmyth Dec 22 '15 at 22:12
  • 13
    Correct @loganfsmyth, React will not reload the script on re-render if the next state also has the script. – Max S. May 22 '16 at 18:55
  • 2
    Here is [why this happens](https://stackoverflow.com/a/64815699/7730507) – chetan Feb 11 '21 at 22:05
  • Why don't you just add it to your public/index.html (create-react-app)? The real question then becomes how to import it? – Vishesh Mangla Jun 13 '23 at 17:43

30 Answers30

784

Edit: Things change fast and this is outdated - see update


Do you want to fetch and execute the script again and again, every time this component is rendered, or just once when this component is mounted into the DOM?

Perhaps try something like this:

componentDidMount () {
    const script = document.createElement("script");

    script.src = "https://use.typekit.net/foobar.js";
    script.async = true;

    document.body.appendChild(script);
}

However, this is only really helpful if the script you want to load isn't available as a module/package. First, I would always:

  • Look for the package on npm
  • Download and install the package in my project (npm install typekit)
  • import the package where I need it (import Typekit from 'typekit';)

This is likely how you installed the packages react and react-document-title from your example, and there is a Typekit package available on npm.


Update:

Now that we have hooks, a better approach might be to use useEffect like so:

useEffect(() => {
  const script = document.createElement('script');

  script.src = "https://use.typekit.net/foobar.js";
  script.async = true;

  document.body.appendChild(script);

  return () => {
    document.body.removeChild(script);
  }
}, []);

Which makes it a great candidate for a custom hook (eg: hooks/useScript.js):

import { useEffect } from 'react';

const useScript = url => {
  useEffect(() => {
    const script = document.createElement('script');

    script.src = url;
    script.async = true;

    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    }
  }, [url]);
};

export default useScript;

Which can be used like so:

import useScript from 'hooks/useScript';

const MyComponent = props => {
  useScript('https://use.typekit.net/foobar.js');

  // rest of your component
}
Community
  • 1
  • 1
Alex McMillan
  • 17,096
  • 12
  • 55
  • 88
  • Thank you. I was thinking about this wrong. I went ahead with this implementation. Just adding the `try{Typekit.load({ async: true });}catch(e){}` after the `appendChild` – ArrayKnight Dec 22 '15 at 22:08
  • 2
    I decided that the "advanced" implementation from TypeKit was more suitable to this approach. – ArrayKnight Dec 22 '15 at 22:19
  • 57
    This does work - to load the script, but how can I get access to the code in the script. For example, I'd like to call a function that lives inside the script, but I'm not able to invoke it inside the component where the script is loaded. – zero_cool Oct 11 '16 at 19:45
  • 3
    When the script is appended to the page it will be executed as normal. For example, if you used this method to download jQuery from a CDN, then after the `componentDidMount` function had downloaded and appended the script to the page, you will have the `jQuery` and `$` objects available globally (ie: on `window`). – Alex McMillan Oct 11 '16 at 19:59
  • This is dangerous since DOM manipulations and Network requests should be done inside `componentDidMount` or it may introduce funky render problems. reference: https://reactjs.org/docs/react-component.html#componentdidmount – Farzad Yousefzadeh Nov 06 '17 at 21:14
  • @FarzadYZ That's an important note to be aware of, but this is not "dangerous" at all as it doesn't use `setState` or manipulate the state of the component in any way. But it's definitely not "the react way" either - it's a hack... and like all hacks, is only valuable in certain situations. – Alex McMillan Nov 15 '17 at 19:36
  • 3
    I had a similar problem using an authentication script and turned out that it might be better to include it in the html file of the root a layer above your react App.js. In case anyone finds this useful. As @loganfsmith mentioned... – devssh Jan 09 '18 at 14:55
  • How do I load multiple scripts through this? – Ashh Jan 23 '18 at 06:20
  • This works fine in this case, ie appending to , but if one needs to add this to one component created by React, then one should use in ComponentDidMount. – BernardA Feb 22 '18 at 13:20
  • 1
    that's Great.. that works for me when i want to load local script, i put the js files in the `public` folder, `js/script.js` and then at the `src` point to this as you normally call scripts from `index.html` ... `script.src = "js/script.js";` – Biskrem Muhammad Oct 14 '18 at 20:24
  • now the component first render for a fraction of seconds the jsx and it shows without the script.js functionalities, then after appending the scripts the jsx shows normally, if you use state to render element on document load or after setTimeout it won't work. so how to prevent the component from show any jsx or show loading while the it appends the scripts. to show jsx – sasha romanov Jul 24 '19 at 00:11
  • I did the same. But it require me to refresh the page to load the script. – Mritunjay Upadhyay Nov 02 '19 at 04:35
  • Sometimes it's better to load a script than include the same lib as react package. Because as a package it would blow up the compiled js code. As a script it would load only on demand. In ideal world it should be included as npm package and loaded on demand but that's more tricky than via script-tag. – Stalinko Jan 21 '20 at 10:17
  • @AlexMcMillan But what about caching? Will it create a new script tag with src url every time the component re-renders? Or what about if we want to reuse the same script in multiple components? I saw some examples that makes use of caching but not really sure what is the purpose of it as I thought browsers already cache all the scripts. https://www.newline.co/fullstack-react/articles/Declaratively_loading_JS_libraries/index.html – Limpuls Mar 09 '20 at 23:20
  • 1
    @zero_cool if script loaded, function can called with window property ex) if (window.whatYouLoadedFunction) { window.whatYouLoadedFunction() } – Sacru2red Nov 06 '20 at 06:37
  • 7
    Why `return () => {document.body.removeChild(script);}` ? Why it's needed that return of the removeChild – pmiranda Mar 02 '21 at 01:39
  • 4
    It's good practice to do cleanup. In my case with NextJS removing the script on navigating to another page meant that the script re-executes when navigating back. – Low Nov 04 '21 at 10:36
  • Is it possible to `getElementById(child_in_the_return)`? – Kindred May 03 '22 at 12:52
  • Doesn't work? Using the code in the script gives "failed to compile, X is not defined.."? – User Jun 04 '22 at 06:51
  • _"t's good practice to do cleanup"_: Note that removing a script tag will _not_ undo what the script has done. I.e. you can not really remove a script from a page. So when changing the `url` the hook will add another script, but not remove the previous one. – kca Jul 30 '23 at 07:50
97

My favorite way is to use React Helmet – it's a component that allows for easy manipulation of the document head in a way you're probably already used to.

e.g.

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <script src="https://use.typekit.net/foobar.js"></script>
                <script>try{Typekit.load({ async: true });}catch(e){}</script>
            </Helmet>
            ...
        </div>
    );
  }
};

https://github.com/nfl/react-helmet

cdcdcd
  • 1,612
  • 13
  • 18
  • 6
    Unfortunately, it is not working... See https://codesandbox.io/s/l9qmrwxqzq – Darkowic Dec 06 '18 at 11:05
  • 4
    @Darkowic, I got your code to work by adding `async="true"` to the ` – Soma Mbadiwe Feb 01 '19 at 05:06
  • @SomaMbadiwe why it works with `async=true` and fails without it? – Webwoman Apr 09 '19 at 19:37
  • @Patrice-Wrex Helmet looks like a great solution to my problem. Can I use it to load local .js files? if so mind sharing an example? – Ricardo Sanchez Jun 08 '19 at 16:42
  • @RicardoSanchez - the `helmet` component in this example has normal `script` elements as its children. So as long as the `src` correctly points to your local files it will work. If you are having trouble resolving scripts in your project make sure webpack or whichever build tools you're using are bundling these files somewhere you can use them. – cdcdcd Jun 08 '19 at 21:20
  • 4
    Tried this, doesn't work for me. I wouldn't recommend using react-helmet for the sole reason that it injects extra properties into the script that can't be removed. This actually break certain scripts, and the maintainers haven't fixed it in years, and refuse to https://github.com/nfl/react-helmet/issues/79 – Philberg Jun 14 '19 at 16:13
  • I am trying to get a local script to do something as simple as logging to the console or writing one test line. But I keep getting the `Unexpected token '<'` error which I understand usually points to an issue within the js file or the path but I know the path is right (I've tried all sorts of combinations) and the js file is simply `document.write('test')`. I have `` – yankeedoodle Apr 06 '22 at 18:10
  • Avoid using react helmet with script. With React Helmet we can declare script tags to load JavaScript files. The problem with that approach is that it's hard to control when a particular script got loaded. Errors during loading are hard to process as well. – Derek Fan Mar 03 '23 at 22:43
78

Further to the answers above you can do this:

import React from 'react';

export default class Test extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.innerHTML = "document.write('This is output by document.write()!')";
    this.instance.appendChild(s);
  }

  render() {
    return <div ref={el => (this.instance = el)} />;
  }
}

The div is bound to this and the script is injected into it.

Demo can be found on codesandbox.io

sidonaldson
  • 24,431
  • 10
  • 56
  • 61
49

This answer explains the why behind this behavior.

Any approach to render the script tag doesn't work as expected:

  1. Using the script tag for external scripts
  2. Using dangerouslySetInnerHTML

Why

React DOM (the renderer for react on web) uses createElement calls to render JSX into DOM elements.

createElement uses the innerHTML DOM API to finally add these to the DOM (see code in React source). innerHTML does not execute script tag added as a security consideration. And this is the reason why in turn rendering script tags in React doesn't work as expected.

For how to use script tags in React check some other answers on this page.

Divyanshu Maithani
  • 13,908
  • 2
  • 36
  • 47
24

If you need to have <script> block in SSR (server-side rendering), an approach with componentDidMount will not work.

You can use react-safe library instead. The code in React will be:

import Safe from "react-safe"

// in render 
<Safe.script src="https://use.typekit.net/foobar.js"></Safe.script>
<Safe.script>{
  `try{Typekit.load({ async: true });}catch(e){}`
}
</Safe.script>
scabbiaza
  • 1,962
  • 1
  • 15
  • 11
21

The answer Alex Mcmillan provided helped me the most but didn't quite work for a more complex script tag.

I slightly tweaked his answer to come up with a solution for a long tag with various functions that was additionally already setting "src".

(For my use case the script needed to live in head which is reflected here as well):

  componentWillMount () {
      const script = document.createElement("script");

      const scriptText = document.createTextNode("complex script with functions i.e. everything that would go inside the script tags");

      script.appendChild(scriptText);
      document.head.appendChild(script);
  }
jake2620
  • 227
  • 2
  • 3
  • 7
    I don't understand why you'd use React at all if you're just dumping inline JS onto the page... ? – Alex McMillan Jan 22 '18 at 23:42
  • 3
    you need to add `document.head.removeChild(script);` in your code, or you will create infinite number of script tag at your html as long as user visits this page route – sasha romanov Jul 24 '19 at 02:27
19

I tried to edit the accepted answer by @Alex McMillan but it won't let me so heres a separate answer where your able to get the value of the library your loading in. A very important distinction that people asked for and I needed for my implementation with stripe.js.

useScript.js

import { useState, useEffect } from 'react'

export const useScript = (url, name) => {

  const [lib, setLib] = useState({})

  useEffect(() => {
    const script = document.createElement('script')

    script.src = url
    script.async = true
    script.onload = () => setLib({ [name]: window[name] })

    document.body.appendChild(script)

    return () => {
      document.body.removeChild(script)
    }
  }, [url])

  return lib

}

usage looks like

const PaymentCard = (props) => {
  const { Stripe } = useScript('https://js.stripe.com/v2/', 'Stripe')
}

NOTE: Saving the library inside an object because often times the library is a function and React will execute the function when storing in state to check for changes -- which will break libs (like Stripe) that expect to be called with specific args -- so we store that in an object to hide that from React and protect library functions from being called.

rayepps
  • 2,072
  • 1
  • 12
  • 22
13

You can also use react helmet

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <meta charSet="utf-8" />
                <title>My Title</title>
                <link rel="canonical" href="http://example.com/example" />
                <script src="/path/to/resource.js" type="text/javascript" />
            </Helmet>
            ...
        </div>
    );
  }
};

Helmet takes plain HTML tags and outputs plain HTML tags. It's dead simple, and React beginner friendly.

9

I created a React component for this specific case: https://github.com/coreyleelarson/react-typekit

Just need to pass in your Typekit Kit ID as a prop and you're good to go.

import React from 'react';
import Typekit from 'react-typekit';

const HtmlLayout = () => (
  <html>
    <body>
      <h1>My Example React Component</h1>
      <Typekit kitId="abc123" />
    </body>
  </html>
);

export default HtmlLayout;
Corey Larson
  • 1,136
  • 9
  • 17
9

There is a very nice workaround using Range.createContextualFragment.

/**
 * Like React's dangerouslySetInnerHTML, but also with JS evaluation.
 * Usage:
 *   <div ref={setDangerousHtml.bind(null, html)}/>
 */
function setDangerousHtml(html, el) {
    if(el === null) return;
    const range = document.createRange();
    range.selectNodeContents(el);
    range.deleteContents();
    el.appendChild(range.createContextualFragment(html));
}

This works for arbitrary HTML and also retains context information such as document.currentScript.

Maximilian Hils
  • 6,309
  • 3
  • 27
  • 46
  • Could you collaborate how is it expected to work please, with usage sample? For me it is not working with passing script and body for example.. – Oleksandr Yefymov Jun 02 '20 at 18:06
7

Here is how I was finally able to add two external JavaScript files in my React JS code:

These are the steps I followed.

Step 1: I installed React-Helmet using npm i react-helmet from the terminal while inside my react-app folder path.

Step 2: I then added import {Helmet} from "react-helmet"; header in my code.

Step 3: Finally, in my code this is how I added the external JS files using Helment

<Helmet>
    <script src = "path/to/my/js/file1.js" type = "text/javascript" />
    <script src = "path/to/my/js/file2.js" type = "text/javascript" />  
</Helmet>
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Joseph
  • 789
  • 1
  • 9
  • 23
5

You can use npm postscribe to load script in react component

postscribe('#mydiv', '<script src="https://use.typekit.net/foobar.js"></script>')
Ashh
  • 44,693
  • 14
  • 105
  • 132
5

You can find best answer at the following link:

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

const loadDynamicScript = (callback) => {
const existingScript = document.getElementById('scriptId');

if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'url'; // URL for the third-party library being loaded.
    script.id = 'libraryName'; // e.g., googleMaps or stripe
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }

  if (existingScript && callback) callback();
};
RobC
  • 22,977
  • 20
  • 73
  • 80
  • 3
    `document.getElementById('scriptId'); ` shouldn't this be `document.getElementById('libraryName');` – Asim K T Jul 14 '20 at 03:19
5

To add script tag or code in head tag <head>, use react-helmet package. it is light and have good documentation.

To add Js code in script tag inside body,

    function htmlDecode(html) {
      return html.replace(/&([a-z]+);/ig, (match, entity) => {
        const entities = { amp: '&', apos: '\'', gt: '>', lt: '<', nbsp: '\xa0', quot: '"' };
        entity = entity.toLowerCase();
        if (entities.hasOwnProperty(entity)) {
          return entities[entity];
        }
        return match;
      });
    }
  render() {
    const scriptCode = `<script type="text/javascript">
          {(function() {
          window.hello={
            FIRST_NAME: 'firstName',
            LAST_NAME: 'lastName',
          };
          })()}
          </script>`
    return(
      <div dangerouslySetInnerHTML={{ __html: this.htmlDecode(scriptCode) }} />;
    );
  }

this code can be tested by console.log(windows.hello)

smsivaprakaash
  • 1,564
  • 15
  • 11
5

Very similar to other answers just using default values to clean up undefined checks

import { useEffect } from 'react'

const useScript = (url, selector = 'body', async = true) => {
  useEffect(() => {
    const element = document.querySelector(selector)
    const script = document.createElement('script')
    script.src = url
    script.async = async
    element.appendChild(script)
    return () => {
      element.removeChild(script)
    }
  }, [url])
}

export default useScript

usage

useScript('/path/to/local/script.js') // async on body
useScript('https://path/to/remote/script.js', 'html') // async on html 
useScript('/path/to/local/script.js', 'html', false) // not async on html.. e.g. this will block
wickdninja
  • 949
  • 1
  • 10
  • 15
3

According to Alex McMillan's solution, I have the following adaptation.
My own environment: React 16.8+, next v9+

// add a custom component named Script
// hooks/Script.js

import { useEffect } from 'react'


// react-helmet don't guarantee the scripts execution order
export default function Script(props) {

  // Ruels: alwasy use effect at the top level and from React Functions
  useEffect(() => {
    const script = document.createElement('script')

    // src, async, onload
    Object.assign(script, props)

    let { parent='body' } = props

    let parentNode = document.querySelector(parent)
    parentNode.appendChild(script)

    return () => {
      parentNode.removeChild(script)
    }
  } )

  return null  // Return null is necessary for the moment.
}

// Use the custom compoennt, just import it and substitute the old lower case <script> tag with the custom camel case <Script> tag would suffice.
// index.js

import Script from "../hooks/Script";
    
<Fragment>
  {/* Google Map */}
  <div ref={el => this.el = el} className="gmap"></div>

  {/* Old html script */}
  {/*<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>*/}

  {/* new custom Script component */}
  <Script async={false} type="text/javascript" src='http://maps.google.com/maps/api/js' />
</Fragment>
Iceberg
  • 2,744
  • 19
  • 19
  • There is one caveat for this component: this Script component can only guarantee the order of it's own siblings. If you use this component multiple times in multiple components of the same page, the scripts blocks might be out of order. The reason is that all the scripts are inserted by document.body.appendChild programmatically, instead of declaratively. Well helmet move all script tags in the head tag, which is not we want. – Iceberg Nov 03 '19 at 04:24
  • Hey @sully, my problem here is having the script added to the DOM severally, the best solution I've seen so far is during Component Unmounting, removing the child element (i.e. the – Eazy Nov 29 '19 at 22:18
2

A bit late to the party but I decided to create my own one after looking at @Alex Macmillan answers and that was by passing two extra parameters; the position in which to place the scripts such as or and setting up the async to true/false, here it is:

import { useEffect } from 'react';

const useScript = (url, position, async) => {
  useEffect(() => {
    const placement = document.querySelector(position);
    const script = document.createElement('script');

    script.src = url;
    script.async = typeof async === 'undefined' ? true : async;

    placement.appendChild(script);

    return () => {
      placement.removeChild(script);
    };
  }, [url]);
};

export default useScript;

The way to call it is exactly the same as shown in the accepted answer of this post but with two extra(again) parameters:

// First string is your URL
// Second string can be head or body
// Third parameter is true or false.
useScript("string", "string", bool);
Kirasiris
  • 523
  • 10
  • 37
2

I recently faced the issue, Tried multiple solutions given here, at last sattled with iframe, Iframe seems to work seamlessly if it you are trying to integrate a js plugin on a specific screen

    <iframe
      id="xxx"
      title="xxx"
      width="xxx"
      height="xxx"
      frameBorder="value"
      allowTransparency
      srcDoc={`
          <!doctype html>
          <html>
          <head>
              <title>Chat bot</title>
              <meta charset="utf-8">
              <meta name="viewport" content="width=device-width">
          </head>
          <body style="width:100%">
              <script type="text/javascript"> 
                ......
                            </script>  
          </body>
          </html>
          `}
 />
fahad991
  • 452
  • 3
  • 8
1
componentDidMount() {
  const head = document.querySelector("head");
  const script = document.createElement("script");
  script.setAttribute(
    "src",
    "https://assets.calendly.com/assets/external/widget.js"
  );
  head.appendChild(script);
}
Cmarl
  • 11
  • 1
  • 2
1

just add in body in html file

<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
ahmed mersal
  • 167
  • 2
  • 3
1

Honestly, for React - don't bother with messing around adding <script> tags to your header. It's a pain in the ass to get a callback when they have loaded fully. Instead, use a package like @charlietango/useScript to load the script when you need it and get a status update when it is completed.

Example usage:

import React from 'react'
import useScript, { ScriptStatus } from '@charlietango/use-script'
 
const Component = () => {
  const [ready, status] = useScript('https://api.google.com/api.js')
 
  if (status === ScriptStatus.ERROR) {
    return <div>Failed to load Google API</div>
  }
 
  return <div>Google API Ready: {ready}</div>
}
 
export default Component

PS. If you're using redux to tell other components when your script has loaded, and are using redux-persist like I was, don't forget to include a modifier on your redux-persist setup that always sets the script loaded redux value to false in the redux backup.

Geoff Davids
  • 887
  • 12
  • 15
1

For a more complete useScript implementation that supports loading status and error handling, check out this from useHooks.

Usage

function App() {
  const status = useScript(
    "https://pm28k14qlj.codesandbox.io/test-external-script.js"
  );
  return (
    <div>
      <div>
        Script status: <b>{status}</b>
      </div>
      {status === "ready" && (
        <div>
          Script function call response: <b>{TEST_SCRIPT.start()}</b>
        </div>
      )}
    </div>
  );
}

Hook

function useScript(src) {
  // Keep track of script status ("idle", "loading", "ready", "error")
  const [status, setStatus] = useState(src ? "loading" : "idle");
  useEffect(
    () => {
      // Allow falsy src value if waiting on other data needed for
      // constructing the script URL passed to this hook.
      if (!src) {
        setStatus("idle");
        return;
      }
      // Fetch existing script element by src
      // It may have been added by another intance of this hook
      let script = document.querySelector(`script[src="${src}"]`);
      if (!script) {
        // Create script
        script = document.createElement("script");
        script.src = src;
        script.async = true;
        script.setAttribute("data-status", "loading");
        // Add script to document body
        document.body.appendChild(script);
        // Store status in attribute on script
        // This can be read by other instances of this hook
        const setAttributeFromEvent = (event) => {
          script.setAttribute(
            "data-status",
            event.type === "load" ? "ready" : "error"
          );
        };
        script.addEventListener("load", setAttributeFromEvent);
        script.addEventListener("error", setAttributeFromEvent);
      } else {
        // Grab existing script status from attribute and set to state.
        setStatus(script.getAttribute("data-status"));
      }
      // Script event handler to update status in state
      // Note: Even if the script already exists we still need to add
      // event handlers to update the state for *this* hook instance.
      const setStateFromEvent = (event) => {
        setStatus(event.type === "load" ? "ready" : "error");
      };
      // Add event listeners
      script.addEventListener("load", setStateFromEvent);
      script.addEventListener("error", setStateFromEvent);
      // Remove event listeners on cleanup
      return () => {
        if (script) {
          script.removeEventListener("load", setStateFromEvent);
          script.removeEventListener("error", setStateFromEvent);
        }
      };
    },
    [src] // Only re-run effect if script src changes
  );
  return status;
}
Tamlyn
  • 22,122
  • 12
  • 111
  • 127
0

for multiple scripts, use this

var loadScript = function(src) {
  var tag = document.createElement('script');
  tag.async = false;
  tag.src = src;
  document.getElementsByTagName('body').appendChild(tag);
}
loadScript('//cdnjs.com/some/library.js')
loadScript('//cdnjs.com/some/other/library.js')
ben
  • 325
  • 1
  • 5
  • 15
0

I had raw html string with javascript/Jquery i installed npm library dangerously-set-html-content npm i dangerously-set-html-content

import InnerHTML from 'dangerously-set-html-content'
<div>
<InnerHTML html={html}/>
</div>

or

import InnerHTML from 'dangerously-set-html-content'
const renderhtml=`<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title> is not defined</title>$(document).ready(function(){    $("button").click(function(){        alert("jQuery is working perfectly.");    });      });</script></head><body>    <button type="button">Test jQuery Code</button></body></html>`

<div>
<InnerHTML html={renderhtml}/>
</div>

Make sure you add jquery cdn to public/index.html file

 <script          src="https://code.jquery.com/jquery-3.3.1.min.js"          integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="          crossorigin="anonymous"          async="true"        ></script>
Xahiid
  • 61
  • 6
0

You could try to use the following:

Make sure you trust the script

 <script>{`
            function myFunction(index, row) {
                return index;
            }
        `}
</script>
PdotNJ
  • 41
  • 4
0

You have to create a component for this script, you call this component as a standard ES6 script tag

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>
                </article>
            </DocumentTitle>
        );

        class Component extend Index.App {  
             <script src="https://use.typekit.net/foobar.js" />
             <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}/>
        }
    }
};
Japs
  • 977
  • 2
  • 10
  • 19
-1

useScript.js hook - helped me. Thanks rayepps

My varian for WayForPay

const {loaded,Wayforpay} = useScript('https://secure.wayforpay.com/server/pay-widget.js','Wayforpay');

click button handler

const openPay = (Wayforpay) =>{
            axios.get('/site/paywfp',{params:{guid:pay.guid}})
                .then(({data}) =>{
    
                    const wfp = new Wayforpay();
    
                    wfp.run(
                        data.option,
                        () => console.log('done'),
                        () => console.log('err'),
                        () => console.log('wait')
                    );
                }
            );
        }

button on loaded set

loaded?<button onClick={()=>openPay(Wayforpay)}>
-2

Solution depends on scenario. Like in my case, I had to load a calendly embed inside a react component.

Calendly looks for a div and reads from it's data-url attribute and loads an iframe inside the said div.

It is all good when you first load the page: first, div with data-url is rendered. Then calendly script is added to body. Browser downloads and evaluates it and we all go home happy.

Problem comes when you navigate away and then come back into the page. This time the script is still in body and browser doesn't re-download & re-evaluate it.

Fix:

  1. On componentWillUnmount find and remove the script element. Then on re mount, repeat the above steps.
  2. Enter $.getScript. It is a nifty jquery helper that takes a script URI and a success callback. Once the script it loaded, it evaluates it and fires your success callback. All I have to do is in my componentDidMount $.getScript(url). My render method already has the calendly div. And it works smooth.
Rohan Bagchi
  • 649
  • 10
  • 17
  • 2
    Adding jQuery to do this is a bad idea, plus your case is very specific to you. In reality there is nothing wrong with adding the Calendly script once as I'm sure the API has a re-detect call. Removing and adding a script over and over again is not correct. – sidonaldson Apr 11 '17 at 15:17
  • @sidonaldson jQuery is not a bad practise if you gotta maintain a project its architecture compound of different frameworks (and libs) not just react, otherwize we need use native js to reach components – Alexey Nikonov Aug 21 '19 at 16:29
-3

I saw the same problem, until I found this package, quite easy to implement, I hope it works as it worked for me :)

https://github.com/gumgum/react-script-tag

import React from 'react';
import Script from '@gumgum/react-script-tag';

import './App.css';

function App() {
  return (
    <div >

      <h1> Graphs</h1>
      <div class="flourish-embed flourish-network" data-src="visualisation/8262420">
        <Script  src"your script"
        </Script>
      </div>
    </div>
  );
}

export default App;
cigien
  • 57,834
  • 11
  • 73
  • 112
Roger SH
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 02 '22 at 14:03
  • @cigien this doesn't even work, you have syntax errors – leopinzon Apr 28 '22 at 18:55
  • 1
    @leopinzon I couldn't say either way; I just edited the answer. The answer was posted by Roger SH. – cigien Apr 28 '22 at 22:27
  • You're right, my apologies @cigien. – leopinzon May 03 '22 at 13:28
-5

You can put your script in an Html file before react is being called.