useScript()
Dynamically load an external script in one line with this React hook. This can be useful to integrate a third party library like Google Analytics or Stripe.
This avoids loading this script in the <head> </head>
on all your pages if it is not necessary.
The Hook
1import { useEffect, useState } from 'react'23export type Status = 'idle' | 'loading' | 'ready' | 'error'4export type ScriptElt = HTMLScriptElement | null56function useScript(src: string): Status {7 const [status, setStatus] = useState<Status>(src ? 'loading' : 'idle')89 useEffect(10 () => {11 if (!src) {12 setStatus('idle')13 return14 }1516 // Fetch existing script element by src17 // It may have been added by another instance of this hook18 let script: ScriptElt = document.querySelector(`script[src="${src}"]`)1920 if (!script) {21 // Create script22 script = document.createElement('script')23 script.src = src24 script.async = true25 script.setAttribute('data-status', 'loading')26 // Add script to document body27 document.body.appendChild(script)2829 // Store status in attribute on script30 // This can be read by other instances of this hook31 const setAttributeFromEvent = (event: Event) => {32 script?.setAttribute(33 'data-status',34 event.type === 'load' ? 'ready' : 'error',35 )36 }3738 script.addEventListener('load', setAttributeFromEvent)39 script.addEventListener('error', setAttributeFromEvent)40 } else {41 // Grab existing script status from attribute and set to state.42 setStatus(script.getAttribute('data-status') as Status)43 }4445 // Script event handler to update status in state46 // Note: Even if the script already exists we still need to add47 // event handlers to update the state for *this* hook instance.48 const setStateFromEvent = (event: Event) => {49 setStatus(event.type === 'load' ? 'ready' : 'error')50 }5152 // Add event listeners53 script.addEventListener('load', setStateFromEvent)54 script.addEventListener('error', setStateFromEvent)5556 // Remove event listeners on cleanup57 return () => {58 if (script) {59 script.removeEventListener('load', setStateFromEvent)60 script.removeEventListener('error', setStateFromEvent)61 }62 }63 },64 [src], // Only re-run effect if script src changes65 )6667 return status68}6970export default useScript
Usage
1import React, { useEffect } from 'react'23import useScript from './useScript'45// it's an example, use your types instead6declare const jQuery: any78export default function Component() {9 // Load the script asynchronously10 const status = useScript(`https://code.jquery.com/jquery-3.5.1.min.js`)1112 useEffect(() => {13 if (typeof jQuery !== 'undefined') {14 // jQuery is loaded => print the version15 alert(jQuery.fn.jquery)16 }17 }, [status])1819 return (20 <div>21 <p>{`Current status: ${status}`}</p>2223 {status === 'ready' && <p>You can use the script here.</p>}24 </div>25 )26}
Created:
7 months ago