React中通过CustomHook引入外部JS API失效问题求助
问题描述
在React项目中引入外部JS脚本实现订阅注册功能,脚本里的DOMContentLoaded和window.load事件监听代码完全不执行,点击Submit按钮无反应,且无法修改外部external.js代码。
React组件代码
import React from 'react' import styles from './MailingList.module.css'; import UseExternalScript from '../../customHook/UseExternalScript/UseExternalScript'; const MailingList = ({ mlistValues }) => { UseExternalScript(['https://ww.example.com/external.js', 'https://www.example.com/external2.js']) return ( <> <div className="ml-wrapper firstMlistForm"> <div className="mlform"> <form className="mlistFormOne twostep"> <div className="email fieldWrap"> <input data-type="email" data-error-text="Please enter a valid email address" required="" className="email" placeholder="Email" name="email" /> </div> <div className="list-values"> </div> <div className="submit"> <input type="submit" className="submit mlistSubmit" value="Submit" /> </div> </form> <div className="terms"> <a href="#" className="terms">terms</a> <p className={`terms-message fadeOut`}>T&C</p> </div> </div> </div> </> ) } export default MailingList
自定义Hook代码(UseExternalScript.js)
import { useEffect } from 'react' const UseExternalScript = (srcArray) => { useEffect(() => { const scritpTags = srcArray.map((src) => { const script = document.createElement('script'); script.src = src; script.async = true; script.onload = () => { console.log(`Script loaded: ${src}`); }; document.body.appendChild(script); return script; }); return () => { scritpTags.forEach((script) => { document.body.removeChild(script); }); } }, [srcArray]) } export default UseExternalScript
外部JS代码片段(external.js)
/****/ document.addEventListener('DOMContentLoaded', function() { const countrySelectList = [...document.querySelectorAll(countrySelector)]; countrySelectList.forEach(select => { /****/ }); [...document.querySelectorAll('label[for=country], label[for=Country]')].forEach((c) => { c.innerHTML = 'country/region'; }); /*****/ }); /*****/ window.addEventListener('load', function(event) { [...document.querySelectorAll('.ml-wrapper a.terms')].forEach((terms) => { terms.addEventListener('click', function(event) { /***/ }, false); }); [...document.querySelectorAll(formSubmitSelector)].forEach((submit_button) => { submit_button.addEventListener('click', function(event) { event.preventDefault(); /***/ }, false); }); });
解决方案
核心原因
DOMContentLoaded和window.load事件只会在页面初始加载时触发一次,React组件渲染时这两个事件早已触发完毕,动态插入的外部脚本里的监听代码自然不会执行。
解决步骤
修改自定义Hook,手动触发事件
在脚本加载完成后,手动触发DOMContentLoaded和window.load事件,让外部脚本的回调执行。修改后的UseExternalScript.js如下:import { useEffect } from 'react' const UseExternalScript = (srcArray) => { useEffect(() => { const loadScripts = async () => { // 按顺序同步加载脚本,避免依赖问题 for (const src of srcArray) { await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = src; script.async = false; script.onload = () => { console.log(`Script loaded: ${src}`); resolve(); }; script.onerror = reject; document.body.appendChild(script); }); } // 手动触发DOMContentLoaded事件 if (document.readyState === 'complete' || document.readyState === 'interactive') { const domEvent = new Event('DOMContentLoaded'); document.dispatchEvent(domEvent); } // 手动触发window.load事件 const loadEvent = new Event('load'); window.dispatchEvent(loadEvent); }; loadScripts(); return () => { srcArray.forEach(src => { const script = document.querySelector(`script[src="${src}"]`); if (script) document.body.removeChild(script); }); } }, [srcArray]) } export default UseExternalScript关键改动:
- 改为同步加载脚本,确保脚本按顺序执行
- 脚本全部加载完成后,手动触发两个全局事件
确保组件渲染完成后加载脚本
若组件是动态渲染(如路由切换场景),可在组件的useEffect中调用Hook,确保DOM已挂载:const MailingList = ({ mlistValues }) => { useEffect(() => { UseExternalScript(['https://ww.example.com/external.js', 'https://www.example.com/external2.js']) }, []) // 原返回内容不变 }备选方案:事件委托
如果手动触发全局事件会影响其他代码,可在React组件中通过事件委托绑定逻辑:const MailingList = ({ mlistValues }) => { useEffect(() => { // 绑定terms点击逻辑 const handleTermsClick = (e) => { if (e.target.matches('.ml-wrapper a.terms')) { // 复制external.js中terms点击的逻辑代码 } } // 绑定submit点击逻辑 const handleSubmit = (e) => { if (e.target.matches('.submit.mlistSubmit')) { e.preventDefault(); // 复制external.js中submit点击的逻辑代码 } } document.addEventListener('click', handleTermsClick); document.addEventListener('click', handleSubmit); return () => { document.removeEventListener('click', handleTermsClick); document.removeEventListener('click', handleSubmit); } }, []) // 原返回内容不变 }此方案无需修改Hook,但需要复制外部脚本的业务逻辑,适合无法触发全局事件的场景。
内容的提问来源于stack exchange,提问作者Nothing
相关产品推荐
相关产品推荐

