You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:59:40