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

如何在React(Gatsby)中实现window滚动监听并解决生产环境报错

Fixing Sticky Header on Scroll in Gatsby/React

Hey there! Let's sort out that sticky header issue you're facing in Gatsby. Your current code works in development but breaks production for two key reasons:

  • Gatsby uses server-side rendering (SSR) during builds, where the window object doesn't exist. Directly referencing it throws an error when Gatsby generates your static pages.
  • Manipulating the DOM directly with document.querySelector conflicts with React's virtual DOM system, which can lead to unexpected behavior as your app updates.

Here's the proper React/Gatsby-friendly way to implement this feature:

Step-by-Step Breakdown

1. Use React State for Sticky Status

Instead of directly adding/removing classes from the DOM, we'll use useState to track whether the header should be sticky. This keeps our UI in sync with React's data flow.

2. Safely Handle Event Listeners with useEffect

useEffect lets us add scroll listeners when the component mounts, and clean them up when it unmounts. We'll also add guards to avoid SSR errors by checking if window exists first.

3. Use React Refs for DOM Access

Instead of hunting for elements with document.querySelector, we'll use useRef to get a direct, React-approved reference to our nav element.

Scroll events fire constantly—adding a throttle limits how often our check function runs, which improves performance.

Full Working Code Example

import { useState, useEffect, useRef } from 'react';
import headerStyles from './your-header-styles.module.css'; // Update this path to your styles

const Header = () => {
  // State to track if header should be sticky
  const [isSticky, setIsSticky] = useState(false);
  // Ref to access the nav DOM element
  const navRef = useRef(null);

  // Throttle function to limit scroll event frequency
  const throttle = (func, delay) => {
    let lastExecution = 0;
    return (...args) => {
      const now = Date.now();
      if (now - lastExecution >= delay) {
        func(...args);
        lastExecution = now;
      }
    };
  };

  const checkScrollPosition = () => {
    // Guard clause to avoid SSR errors
    if (typeof window === 'undefined') return;

    const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
    if (viewportWidth > 1100) {
      const scrollPosition = Math.round(window.scrollY);
      // Update state based on scroll position
      setIsSticky(scrollPosition > 100);
    } else {
      // Reset sticky state on smaller screens
      setIsSticky(false);
    }
  };

  useEffect(() => {
    // Create a throttled version of our check function
    const throttledCheck = throttle(checkScrollPosition, 100);

    // Add scroll listener only if window exists
    if (typeof window !== 'undefined') {
      window.addEventListener('scroll', throttledCheck);
      // Run once on mount to set initial state
      checkScrollPosition();
    }

    // Cleanup: Remove listener when component unmounts
    return () => {
      if (typeof window !== 'undefined') {
        window.removeEventListener('scroll', throttledCheck);
      }
    };
  }, []); // Empty dependency array = runs once on mount, cleanup on unmount

  return (
    <nav
      ref={navRef}
      id="nav"
      className={`${isSticky ? headerStyles.sticky : ''}`}
    >
      {/* Add your navigation content here */}
    </nav>
  );
};

export default Header;

Key Improvements Explained

  • SSR Safety: Every time we reference window, we check if it exists first—this prevents errors during Gatsby's build process.
  • React State: The isSticky state controls the header's class, so React handles updating the DOM correctly.
  • Refs: navRef gives us a safe way to access the nav element without querying the entire document.
  • Throttling: The throttle function ensures our check runs at most once every 100ms, reducing unnecessary computations.
  • Cleanup: The useEffect cleanup function removes the scroll listener when the component unmounts, preventing memory leaks.

This approach will work seamlessly in both development and production environments, and aligns with React's best practices.

内容的提问来源于stack exchange,提问作者JFelix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 16:02:41