componentDidMount是否导致html.script消失?React/Gatsby脚本挂载异常
Hey Darren, let's break down why your external script is disappearing on dynamically generated Gatsby pages—this is a super common quirk with how Gatsby handles static rendering and client-side hydration!
The Root Cause
When you load your component on the static index.js page, Gatsby builds that page fully statically, and the script runs after the DOM is ready with no hydration conflicts. But for dynamically generated pages created via createPages, Gatsby first serves static HTML, then "hydrates" it with React on the client. If your script modifies the DOM after hydration starts, React might overwrite those changes because it expects the DOM to match its virtual DOM state. Also, Gatsby's client-side navigation (when you click between pages) doesn't do a full page reload, so your script might not re-run correctly when the component mounts again.
Fixes to Try
1. Ensure Scripts Run After Hydration & On Route Changes
If you're using a class component, update your componentDidMount and add route listening to re-run the script when navigating to the dynamic page:
componentDidMount() { // Only run in the browser (Gatsby static build skips this) if (typeof window !== 'undefined') { this.loadAndInitScript(); // Listen for route changes to re-initialize the script this.unsubscribe = this.props.history.listen(() => { this.loadAndInitScript(); }); } } componentWillUnmount() { // Clean up the route listener and existing script if (this.unsubscribe) this.unsubscribe(); const existingScript = document.getElementById('your-unique-script-id'); if (existingScript) existingScript.remove(); } loadAndInitScript() { const scriptId = 'your-unique-script-id'; let script = document.getElementById(scriptId); if (!script) { script = document.createElement('script'); script.src = 'https://your-external-script-url.com/script.js'; script.id = scriptId; script.async = true; document.body.appendChild(script); } // Wait for the script to load, then manually initialize its content const initContent = () => { // If your script has an explicit init function, call it here if (window.yourScriptInitFunction) { window.yourScriptInitFunction(); } // Force a component update to make React recognize the script-generated DOM this.forceUpdate(); }; // Handle script load for modern browsers and IE if (script.readyState) { script.onreadystatechange = () => { if (script.readyState === 'loaded' || script.readyState === 'complete') { script.onreadystatechange = null; initContent(); } }; } else { script.onload = initContent; } }
2. For Function Components: Use useEffect with Route Listening
If you've migrated to function components, use useEffect paired with useLocation to re-run the script whenever the route changes:
import { useEffect } from 'react'; import { useLocation } from '@reach/router'; const YourScriptComponent = () => { const location = useLocation(); useEffect(() => { const loadScript = () => { const scriptId = 'your-unique-script-id'; let script = document.getElementById(scriptId); if (!script) { script = document.createElement('script'); script.src = 'https://your-external-script-url.com/script.js'; script.id = scriptId; script.async = true; document.body.appendChild(script); } const initScript = () => { if (window.yourScriptInitFunction) { window.yourScriptInitFunction(); } }; if (script.readyState) { script.onreadystatechange = () => { if (script.readyState === 'loaded' || script.readyState === 'complete') { script.onreadystatechange = null; initScript(); } }; } else { script.onload = initScript; } }; loadScript(); // Clean up script on unmount return () => { const script = document.getElementById('your-unique-script-id'); if (script) script.remove(); }; }, [location.pathname]); // Re-run effect when the route changes return <div id="script-target-element"></div>; // Element your script targets }; export default YourScriptComponent;
3. Double-Check Component Keys
Make sure when you render your component in the dynamic template, you pass a unique key prop (like the page's slug or ID). This helps React track the component correctly during navigation, preventing unexpected re-mounts or DOM resets:
// In your dynamic template page <YourScriptComponent key={page.node.slug} />
Quick Recap
- Gatsby's hydration can overwrite script-generated DOM if timing is off
- Always run scripts in the browser only (check
typeof window) - Listen for route changes to re-initialize scripts on client-side navigation
- Manually call your script's init function after it loads to ensure content sticks
内容的提问来源于stack exchange,提问作者Darren

