React 如何使用脚本钩子处理多个调用



我正在使用 React useScript 钩子(来自 useHooks 网站(。它允许轻松加载外部脚本并在加载后缓存它们。

它工作正常,但是我发现一个边缘情况导致我一些问题。 问题出在caching of scripts.

如果我使用如下所示useScript在页面中加载了 2 次组件:

const ScriptDemo = src => {
const [loaded, error] = useScript("https://hzl7l.codesandbox.io/test-external-script.js");
return (
<div>
<div>
Script loaded: <b>{loaded.toString()}</b>
</div>
<br />
{loaded && !error && (
<div>
Script function call response: <b>{TEST_SCRIPT.start()}</b>
</div>
)}
</div>
);
};
function App() {
return (
<div>
<ScriptDemo />
<ScriptDemo />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

您可以在此处查看和复制:https://codesandbox.io/s/usescript-hzl7l

如果我的App只有一个ScriptDemo没关系,但是有两个或更多会使它失败。

实际上,流程将是:

脚本
  • 演示 -脚本缓存> ? 否 -> 将脚本添加到缓存 ->获取它 ->渲染
  • 脚本
  • 演示2 -脚本缓存>? 是的 ->渲染(但它没有完成加载 然而.

修复它的一种方法是将useScript钩更改为仅在成功onScriptLoad回调后缓存脚本。 此方法的问题在于外部脚本将被调用两次。 看这里: https://codesandbox.io/s/usescript-0yior

脚本
  • 演示 -脚本缓存> ? 否 ->获取它 -> 将脚本添加到缓存 ->渲染
  • 脚本
  • 演示 -脚本缓存> ? 否 ->获取它 -> 将脚本添加到缓存 ->渲染

我想过缓存脚本 src 和加载布尔值,但这意味着设置超时处理,在我看来它变得非常复杂。

那么,更新钩子以便只加载一次外部脚本但确保它正确加载的最佳方法是什么?

useScript模块中,我们需要跟踪加载脚本的状态。

因此,我们现在需要保留一个表示加载状态的对象,而不是简单的字符串数组cachedScripts

此修改后的useScript实现将解决此问题:

import { useState, useEffect } from 'react';
let cachedScripts = {};
export function useScript(src) {
// Keeping track of script loaded and error state
const [state, setState] = useState({
loaded: false,
error: false
});
useEffect(
() => {
const onScriptLoad = () => {
cachedScripts[src].loaded = true;
setState({
loaded: true,
error: false
});
};
const onScriptError = () => {
// Remove it from cache, so that it can be re-attempted if someone tries to load it again
delete cachedScripts[src];
setState({
loaded: true,
error: true
});
};
let scriptLoader = cachedScripts[src];
if(scriptLoader) { // Loading was attempted earlier
if(scriptLoader.loaded) { // Script was successfully loaded
setState({
loaded: true,
error: false
});
} else { //Script is still loading
let script = scriptLoader.script;
script.addEventListener('load', onScriptLoad);
script.addEventListener('error', onScriptError);
return () => {
script.removeEventListener('load', onScriptLoad);
script.removeEventListener('error', onScriptError);
};
}
} else {
// Create script
let script = document.createElement('script');
script.src = src;
script.async = true;
// Script event listener callbacks for load and error

script.addEventListener('load', onScriptLoad);
script.addEventListener('error', onScriptError);
// Add script to document body
document.body.appendChild(script);
cachedScripts[src] = {loaded:false, script};
// Remove event listeners on cleanup
return () => {
script.removeEventListener('load', onScriptLoad);
script.removeEventListener('error', onScriptError);
};
}
},
[src] // Only re-run effect if script src changes
);
return [state.loaded, state.error];
}

编辑:

转到useHooks的GitHub页面以建议此改进,并发现有人已经发布了类似的修复程序:

https://gist.github.com/gragland/929e42759c0051ff596bc961fb13cd93#gistcomment-2975113

相关内容

  • 没有找到相关文章

最新更新