在Gatsby JS中使用外部脚本(JavaScript)

在Gatsby JS中使用外部脚本(JavaScript),javascript,reactjs,react-hooks,gatsby,Javascript,Reactjs,React Hooks,Gatsby,我正在尝试在我的盖茨比应用程序上加载JavaScript代码。在根文件夹的静态文件夹中,我创建了一个名为script.js的代码。下面是其中的片段: window.addEventListener("scroll", function(e) { const navBar = document.querySelector(".navbar"); if (this.scrollY > 10) { navBar.classList.add(

我正在尝试在我的盖茨比应用程序上加载JavaScript代码。在根文件夹的静态文件夹中,我创建了一个名为
script.js
的代码。下面是其中的片段:

window.addEventListener("scroll", function(e) {
  const navBar = document.querySelector(".navbar");
  if (this.scrollY > 10) {
    navBar.classList.add("active");
  } else {
    navBar.classList.remove("active");
  }
})
然后在我的
布局
组件上,我尝试使用头盔来包含以下内容:

import React, { useEffect } from "react"
import { withPrefix, Link } from "gatsby"
import Helmet from "react-helmet"
import Navbar from '../components/Navbar'
import Footer from '../components/Footer'

const Layout = ({ children }) => {

    <Helmet>
      <script src={withPrefix('script.js')} type="text/javascript" />
   </Helmet>

  let AOS;
  useEffect(() => {
    const AOS = require("aos");
    AOS.init({
      once: true,
    });
  }, []);


  useEffect(() => {
    if (AOS) {
      AOS.refresh();
    }
  });

  return (
     <>
     <Navbar />
     { children}
     <Footer />
     </>
  )
  
}

export default Layout
我不确定是否应该将脚本放在匿名函数中以进行此调用,但如何修复此问题

更新:

正如@Ferran所说,我需要在我的应用程序中使用脚本代码作为钩子。不确定我是否做对了,但以下是我做的步骤

在我的
Navbar.js
中,我创建了一个useState钩子,它将处理调整窗口大小的功能:

import React, { useEffect, useState } from "react"
import { Link } from 'gatsby'
import useWindowSize from '../../static/script.js'

const Navbar = () => {

 const [navBarClass, setNavBarClass] = useState("")
 const { height } = useWindowSize()

  useEffect(()=>{
   if(height > 10)setNavBarClass("active")
  }, [height])


  return (
        <header className="header sticky-header">
<nav className={`navbar navbar-expand-lg fixed-top py-3 ${navBarClass}`}>
    <div class="container container-wide">
        <Link to="/"><img src={MainLogo} alt="" /></Link>
      <button type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation" className="navbar-toggler navbar-toggler-right"><i className="fa fa-bars"></i></button>

      <div id="navbarSupportedContent" className="collapse navbar-collapse">
        <ul className="navbar-nav ms-auto">
          <li className="nav-item active"><a href="#" class="nav-link text-uppercase font-weight-bold">Home <span class="sr-only">(current)</span></a></li>
          <li className="nav-item"><a href="#" class="nav-link text-uppercase font-weight-bold">About</a></li>
          <li className="nav-item"><a href="#" class="nav-link text-uppercase font-weight-bold">Gallery</a></li>
          <li className="nav-item"><a href="#" class="nav-link text-uppercase font-weight-bold">Portfolio</a></li>
          <li className="nav-item"><a href="#" class="nav-link text-uppercase font-weight-bold">Contact</a></li>
        </ul>
      </div>
    </div>
  </nav>

        </header>
  )

}

export default Navbar
然后回到
Navbar.js
我将其作为组件导入:

import useWindowSize from '../../static/script.js'
我这样做对吗?

是一个组件,因此必须将其放在
return
语句中:

  return (
     <>
     <Helmet>
       <script src={withPrefix('script.js')} type="text/javascript" />
     </Helmet>
     <Navbar />
     { children}
     <Footer />
     </>
  )
useCrollPosition
是一个自定义挂钩,看起来可能像:

import { useLayoutEffect, useRef } from 'react';

const isBrowser = typeof window !== `undefined`;

const getScrollPosition = ({ element, useWindow }) => {
  if (!isBrowser) return { x: 0, y: 0 };

  const target = element ? element.current : document.body,
    position = target.getBoundingClientRect();

  return useWindow
    ? { x: window.scrollX, y: window.scrollY }
    : { x: position.left, y: position.top };
};

export const useScrollPosition = (effect, deps, element, useWindow, wait) => {
  const position = useRef(getScrollPosition({ useWindow }));
  let throttleTimeout = null;

  const callBack = () => {
    const currentPosition = getScrollPosition({ element, useWindow });

    effect({ previousPosition: position.current, currentPosition: currentPosition });
    position.current = currentPosition;
    throttleTimeout = null;
  };

  useLayoutEffect(() => {
    const handleScroll = () => {
      if (wait && !throttleTimeout) throttleTimeout = setTimeout(callBack, wait);
      else callBack();
    };

    window.addEventListener(`scroll`, handleScroll);

    return () => window.removeEventListener(`scroll`, handleScroll);
  }, deps);
};
基本上,您是在React的生态系统中包装计算
窗口
内容的逻辑,使用状态,这不会破坏您的补水

这样,您就创建了一个保存
nav
类名的状态,初始设置为空(
const[navBarClass,setNavBarClass]=useState(“”
),以及一个保存当前滚动位置的状态(
const[scroll,setScroll]=useState(0)
),初始设置为
0

另一方面,每当
窗口的
滚动
更改时(用户正在滚动),就会触发
useffect
钩子,这是由
deps
数组(
[scroll]
)控制的,如果滚动条大于或不大于10,则保持设置/删除新类名的逻辑

由于类名状态已更改,组件将再次重新水化,实时显示/隐藏类名。最后,计算窗口参数的逻辑由自定义挂钩控制,其内部逻辑不属于您的组件

附言:例如,再水化问题是当您导航到一个页面时,一旦返回到上一个页面,您将看不到某些组件,因为它们不会由于此问题而呈现(再水化)


步骤:

  • 在项目中的任意位置创建一个文件,并将其命名为
    useCollPosition.js

  • 粘贴以下代码:

      import { useLayoutEffect, useRef } from 'react';
    
      const isBrowser = typeof window !== `undefined`;
    
      const getScrollPosition = ({ element, useWindow }) => {
        if (!isBrowser) return { x: 0, y: 0 };
    
        const target = element ? element.current : document.body,
          position = target.getBoundingClientRect();
    
        return useWindow
          ? { x: window.scrollX, y: window.scrollY }
          : { x: position.left, y: position.top };
      };
    
      export const useScrollPosition = (effect, deps, element, useWindow, wait) => {
        const position = useRef(getScrollPosition({ useWindow }));
        let throttleTimeout = null;
    
        const callBack = () => {
          const currentPosition = getScrollPosition({ element, useWindow });
    
          effect({ previousPosition: position.current, currentPosition: currentPosition });
          position.current = currentPosition;
          throttleTimeout = null;
        };
    
        useLayoutEffect(() => {
          const handleScroll = () => {
            if (wait && !throttleTimeout) throttleTimeout = setTimeout(callBack, wait);
            else callBack();
          };
    
          window.addEventListener(`scroll`, handleScroll);
    
          return () => window.removeEventListener(`scroll`, handleScroll);
        }, deps);
      };
    
  • 将其导入到所需组件中,如下所示:

      import { useScrollPosition } from '/path/to/useScrollPosition/useScrollPosition';
    
  • 使用它


如果我将代码放在这里,是否还需要包含script.js:在script.js中?不,您可以使用自定义钩子删除脚本。您只需要创建一个文件来放置代码并将其导入到您的组件中,所以我将把它作为组件导入?没有URL,里面的代码。。。您只需创建一个文件,将其命名为
useWindowsSize.js
,粘贴代码,然后将其导入组件中,如
import{useWindowsSize}from”/path/to/useWindowsSize.js“
我更新了上面的问题,请告诉我是否正确
import { useLayoutEffect, useRef } from 'react';

const isBrowser = typeof window !== `undefined`;

const getScrollPosition = ({ element, useWindow }) => {
  if (!isBrowser) return { x: 0, y: 0 };

  const target = element ? element.current : document.body,
    position = target.getBoundingClientRect();

  return useWindow
    ? { x: window.scrollX, y: window.scrollY }
    : { x: position.left, y: position.top };
};

export const useScrollPosition = (effect, deps, element, useWindow, wait) => {
  const position = useRef(getScrollPosition({ useWindow }));
  let throttleTimeout = null;

  const callBack = () => {
    const currentPosition = getScrollPosition({ element, useWindow });

    effect({ previousPosition: position.current, currentPosition: currentPosition });
    position.current = currentPosition;
    throttleTimeout = null;
  };

  useLayoutEffect(() => {
    const handleScroll = () => {
      if (wait && !throttleTimeout) throttleTimeout = setTimeout(callBack, wait);
      else callBack();
    };

    window.addEventListener(`scroll`, handleScroll);

    return () => window.removeEventListener(`scroll`, handleScroll);
  }, deps);
};
  import { useLayoutEffect, useRef } from 'react';

  const isBrowser = typeof window !== `undefined`;

  const getScrollPosition = ({ element, useWindow }) => {
    if (!isBrowser) return { x: 0, y: 0 };

    const target = element ? element.current : document.body,
      position = target.getBoundingClientRect();

    return useWindow
      ? { x: window.scrollX, y: window.scrollY }
      : { x: position.left, y: position.top };
  };

  export const useScrollPosition = (effect, deps, element, useWindow, wait) => {
    const position = useRef(getScrollPosition({ useWindow }));
    let throttleTimeout = null;

    const callBack = () => {
      const currentPosition = getScrollPosition({ element, useWindow });

      effect({ previousPosition: position.current, currentPosition: currentPosition });
      position.current = currentPosition;
      throttleTimeout = null;
    };

    useLayoutEffect(() => {
      const handleScroll = () => {
        if (wait && !throttleTimeout) throttleTimeout = setTimeout(callBack, wait);
        else callBack();
      };

      window.addEventListener(`scroll`, handleScroll);

      return () => window.removeEventListener(`scroll`, handleScroll);
    }, deps);
  };
  import { useScrollPosition } from '/path/to/useScrollPosition/useScrollPosition';