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 />
);