React useScript hook
React, Hooks, Effect, State, Event · Oct 8, 2021

Dynamically loads an external script.
- Use the
useState()
hook to create a state variable for the load status of the script. - Use the
useEffect()
hook to handle all the logic for loading and unloading the script anytime thesrc
changes. - If no
src
value is present, set thestatus
to'idle'
and return. - Use
Document.querySelector()
to check if a<script>
element with the appropriatesrc
value exists. - If no relevant element exists, use
Document.createElement()
to create one and give it the appropriate attributes. - Use the
data-status
attribute as a way to indicate the status of the script, setting it to'loading'
initially. - If a relevant element exists, skip initialization and update the
status
from itsdata-status
attribute. This ensures that no duplicate element will be created. - Use
EventTarget.addEventListener()
to listen for'load'
and'error'
events and handle them by updating thedata-status
attribute and thestatus
state variable. - Finally, when the component unmounts, use
Document.removeEventListener()
to remove any listeners bound to the element.
const useScript = src => { const [status, setStatus] = React.useState(src ? 'loading' : 'idle'); React.useEffect(() => { if (!src) { setStatus('idle'); return; } let script = document.querySelector(`script[src="${src}"]`); if (!script) { script = document.createElement('script'); script.src = src; script.async = true; script.setAttribute('data-status', 'loading'); document.body.appendChild(script); const setDataStatus = event => { script.setAttribute( 'data-status', event.type === 'load' ? 'ready' : 'error' ); }; script.addEventListener('load', setDataStatus); script.addEventListener('error', setDataStatus); } else { setStatus(script.getAttribute('data-status')); } const setStateStatus = event => { setStatus(event.type === 'load' ? 'ready' : 'error'); }; script.addEventListener('load', setStateStatus); script.addEventListener('error', setStateStatus); return () => { if (script) { script.removeEventListener('load', setStateStatus); script.removeEventListener('error', setStateStatus); } }; }, [src]); return status; }; const script = 'data:text/plain;charset=utf-8;base64,KGZ1bmN0aW9uKCl7IGNvbnNvbGUubG9nKCdIZWxsbycpIH0pKCk7'; const Child = () => { const status = useScript(script); return <p>Child status: {status}</p>; }; const MyApp = () => { const status = useScript(script); return ( <> <p>Parent status: {status}</p> <Child /> </> ); }; ReactDOM.createRoot(document.getElementById('root')).render( <MyApp /> );