Skip to content

Instantly share code, notes, and snippets.

@gragland
Last active December 22, 2022 10:53
Show Gist options
  • Save gragland/929e42759c0051ff596bc961fb13cd93 to your computer and use it in GitHub Desktop.
Save gragland/929e42759c0051ff596bc961fb13cd93 to your computer and use it in GitHub Desktop.
import { useState, useEffect } from 'react';
// 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;
}
@Ladvace
Copy link

Ladvace commented Sep 15, 2021

If I try using it and then using an useEffect in the same file where I call it I get the following error: React hooks error: Rendered more hooks than during the previous render

@hieund20
Copy link

Codesanbox demo page is not working

@MeikyuuTrader
Copy link

How did you manage to deal with cors errors? Trying to load in google maps API with the useScript hook, but I'm getting a cors error.

On the react docs, when using a standard script tag, it says to add the crossorigin field like so<script crossorigin src="..."></script>. Can the same be done in the above hook?

@lucien-perouze-cezembre

Yes you can add whatever props you want on the script tag. On my version you can just add script.crossorigin = "use-credentials" it will add the property on the tag. But are you sure that's your problem ? I'm familiar with Google Map API and never had to define CORS strategy on the client side. You may have forgotten to allow your domain name in the Google Api's administration console. CORS is a security handle by the server and Google only allow domains that have been specified for your application.

@Angelk90
Copy link

@gragland :
If I wanted to do something like this:

const [ status1, status2, status3 ] = useScriptMulti(
"script1.js","script2.js","script3.js"
);

Or

const [ status1, status2, status3 ] = useScriptMulti([
"script1.js","script2.js","script3.js"
]);

How could I do such a hook, with below using useScript?

@harishkotagiri
Copy link

used this code https://usehooks.com/useScript/ in my application, but am getting below error:
'TEST_SCRIPT' is not defined no-undef
Am getting above error here:
{status === "ready" && (
<div style={{marginTop: 20, fontSize: 20}}>
Script function call response: {TEST_SCRIPT.start()}

)}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment