React useScript Hook
动态加载外部脚本。
- 使用
useState()
hook 创建一个用于脚本加载状态的状态变量。 - 使用
useEffect()
hook 处理加载和卸载脚本的所有逻辑,任何时候src
发生变化都会触发。 - 如果没有
src
值,将status
设置为'idle'
并返回。 - 使用
Document.querySelector()
检查是否存在具有相应src
值的<script>
元素。 - 如果不存在相关元素,使用
Document.createElement()
创建一个并赋予适当的属性。 - 使用
data-status
属性来指示脚本的状态,初始设置为'loading'
。 - 如果存在相关元素,则跳过初始化并从其
data-status
属性更新status
。这样可以确保不会创建重复的元素。 - 使用
EventTarget.addEventListener()
监听'load'
和'error'
事件,并通过更新data-status
属性和status
状态变量来处理它们。 - 最后,当组件卸载时,使用
Document.removeEventListener()
移除绑定到元素的任何监听器。
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);
```javascript
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
const useScript = (src) => {
const [status, setStatus] = useState('loading');
useEffect(() => {
const script = document.createElement('script');
script.src = src;
script.async = true;
const setStateStatus = (event) => {
setStatus(event.type === 'load' ? 'ready' : 'error');
};
script.addEventListener('load', setStateStatus);
script.addEventListener('error', setStateStatus);
document.body.appendChild(script);
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>子组件状态: {status}</p>;
};
const MyApp = () => {
const status = useScript(script);
return (
<>
<p>父组件状态: {status}</p>
<Child />
</>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(
<MyApp />
);