Javascript 如何在header组件(ReactJS/Next.js)中使用effect

Javascript 如何在header组件(ReactJS/Next.js)中使用effect,javascript,node.js,reactjs,next.js,use-effect,Javascript,Node.js,Reactjs,Next.js,Use Effect,我有一个header.js组件,它出现在我的next.js/react项目的每个页面上 在里面,我创建了一个带有旋转的打字机效果,所以为了启动它,我在useEffect中添加了如下代码: export default function Header () { useEffect(() => { let delay = 2200; let input = document.getElementsByClassName('myButton')[0], i

我有一个header.js组件,它出现在我的next.js/react项目的每个页面上

在里面,我创建了一个带有旋转的打字机效果,所以为了启动它,我在useEffect中添加了如下代码:

export default function Header () {

  useEffect(() => {

    let delay = 2200;
    let input = document.getElementsByClassName('myButton')[0],
        index = 0;
    const cycleText = () =>{
      let randomItem = items[Math.floor(Math.random() * items.length)];

      button.setAttribute('value', "Click " + randomItem);
      index++;
      (index === items.length) ? index = 0 : "";
      setTimeout(cycleText, delay);
    }

    cycleText();
  }, []);

  return ( ...
要像使用jQuery一样重新创建某种类型的
文档.ready
,尽管我在使用
useffect
时遇到了两个问题,一个是如果我更改页面,标题保持不变,但效果代码看起来是死的(不再显示任何文本),另一个是,如果我回到主页,代码在第一次加载时工作正常,现在它显示了快速成功中的两个旋转实例(好像有两个useEffect同时运行,一个在另一个之上,或者类似的东西)

我越来越了解useEffect是如何工作的,我知道[]使它在页面加载时运行一次,但我认为这就是我的案例所需要的,看起来我错了。我是否应该在[]中传递可以解决上述两个问题的内容?我尝试使用next.js router,传递router.pathname,让它在每次页面更改时再次运行,但不起作用

更新:我注意到上述两种奇怪的行为只有在从主页转到预先生成的页面(通过staticpath和staticProps)并返回时才会发生。 如果我从主页转到用户仪表板(服务器端渲染)并返回,它不会与标题中的useEffect混淆

更新2:我发现是我在\u app.js中的retain scroll position代码确实针对预生成的页面以及主页,但实际上不是用户仪表板页面。现在,我必须弄清楚如何通过让组件重新渲染来保持滚动位置,因为我已经看到,由于同样的问题,另一段代码使边栏粘滞,但没有启动

这是我的\u app.js代码:

import React, { useRef, useEffect, memo } from 'react'
import { Provider as NextAuthProvider } from 'next-auth/client'
import { SWRConfig } from 'swr'
import Router, { useRouter } from 'next/router'
import '../public/style.scss'
import { AnimatePresence } from "framer-motion"

export default function MyApp ({ Component, pageProps }) {

  const router = useRouter()
  const retainedComponents = useRef({})

  var isRetainableRoute = false

  if (router.pathname == '/' || router.asPath.includes('/author/') || router.asPath.includes('/video/')){
    isRetainableRoute = true
  }

  // Add Component to retainedComponents if we haven't got it already
  if (isRetainableRoute && !retainedComponents.current[router.asPath]) {
    const MemoComponent = memo(Component)
    retainedComponents.current[router.asPath] = {
      component: <MemoComponent {...pageProps} />,
      scrollPos: 0
    }
  }

  // Save the scroll position of current page before leaving
  const handleRouteChangeStart = url => {
    if (isRetainableRoute) {
      retainedComponents.current[router.asPath].scrollPos = window.scrollY
    }
  }

  // Save scroll position - requires an up-to-date router.asPath
  useEffect(() => {
    router.events.on('routeChangeStart', handleRouteChangeStart)
    return () => {
      router.events.off('routeChangeStart', handleRouteChangeStart)
    }
  }, [router.asPath])

  // Scroll to the saved position when we load a retained component
  useEffect(() => {
    if (isRetainableRoute) {
      window.scrollTo(0, retainedComponents.current[router.asPath].scrollPos)
    }
  }, [Component, pageProps])

  return (
    <NextAuthProvider
      options={{
        clientMaxAge: 60,
        keepAlive: 61
      }}
      session={pageProps.session}
      >
      <SWRConfig 
        value={{
          revalidateOnFocus: false
        }}
      >
        <AnimatePresence exitBeforeEnter>
          <div>
            <div style={{ display: isRetainableRoute ? 'block' : 'none' }}>
              {Object.entries(retainedComponents.current).map(([path, c]) => (
                <div
                  key={path}
                  style={{ display: router.asPath === path ? 'block' : 'none' }}
                >
                  {c.component}
                </div>
              ))}
            </div>
            {!isRetainableRoute && <Component {...pageProps} />}
          </div>
        </AnimatePresence>
      </SWRConfig>
    </NextAuthProvider>
  )
}
import React,{useRef,useffect,memo}来自“React”
从“下一个身份验证/客户端”导入{Provider as nextuthprovider}
从“swr”导入{SWRConfig}
从“下一个/路由器”导入路由器{useRouter}
导入“../public/style.scss”
从“帧运动”导入{AnimatePresence}
导出默认函数MyApp({Component,pageProps}){
const router=useRouter()
const retainedComponents=useRef({})
var isRetainableRoute=false
if(router.pathname=='/'| | | router.asPath.includes('/author/'))| | | router.asPath.includes('/video/')){
isRetainableRoute=true
}
//如果我们还没有将组件添加到retainedComponents中
if(isRetainableRoute&!retainedComponents.current[router.asPath]){
常量备忘录组件=备忘录(组件)
retainedComponents.current[路由器.asPath]={
组成部分:,
位置:0
}
}
//离开前保存当前页面的滚动位置
常量handleRouteChangeStart=url=>{
如果(isRetainableRoute){
retainedComponents.current[router.asPath].scrollPos=window.scrollY
}
}
//保存滚动位置-需要最新的router.asPath
useffect(()=>{
路由器.events.on('routeChangeStart',HandlerRouteChangeStart)
return()=>{
router.events.off('routeChangeStart',HandlerRouteChangeStart)
}
},[router.asPath])
//加载保留组件时,滚动至保存位置
useffect(()=>{
如果(isRetainableRoute){
scrollTo(0,retainedComponents.current[router.asPath].scrollPos)
}
},[Component,pageProps])
返回(
{Object.entries(retainedComponents.current).map([path,c])=>(
{c.component}
))}
{!isRetainableRoute&&}
)
}

通过在
setTimeout
中递归调用
cycleText
,您创建了一个永不结束的函数,即使在头组件卸载时(在更改页面时发生)。当移动到再次包含页眉组件的页面时,将创建该页眉组件,并在装入时再次执行
useffect
,从而启动另一个无休止的
cycleText
函数,以此类推

您可以在
cycleText
函数中放置
console.log
,以查看当您移动到另一个页面并返回到具有页眉的页面时,它会越来越快地进行日志记录

我认为最好避免使用递归无止境函数来避免使用堆栈内存,而使用
setInterval
。更重要的是:不要忘记在卸载组件时停止您的功能。对于
useffect
它在返回时完成,您可以在那里调用
clearInterval

您可以这样做:

useffect(()=>{
设延时=2200;
让输入=document.getElementsByCassName('myButton')[0],
指数=0;
常量间隔=设置间隔(()=>{
让randomItem=items[Math.floor(Math.random()*items.length)];
按钮.setAttribute('值',“单击”+随机项);
索引++;
如果(索引>=items.length){
索引=0
}
},延误);
return()=>{
间隔时间;
};
}, []);

您可以在一个组件中声明多个
useffect()
。我想这对你有帮助

在标题组件中,保持第一个
useffect()
不变。添加第二个,如下所示:

useEffect(() => {
  ... code to update state
}, [updatedProp]);

这里的关键是数组中的
updatedProp
项。这告诉useEffect在特定属性或变量更改时触发。将
updatedProp
替换为您希望此useEffect触发的变量/道具。

遗憾的是,它不起作用,无论如何,感谢我使用它,因为它肯定比超时更有意义。阿尔托一世