允许组件检测盖茨比中的路线变化

时间:2020-04-17 14:45:35

标签: reactjs gatsby

在Gatsby项目中,我有一个标头组件,该组件在每个页面上都保持不变。标头具有用于显示导航的模式。每当路由更改时,我都需要将isOpen状态设置为false,以便关闭导航模式。由于不仅可以通过单击模式中的链接来更改路线,而且还可以通过使用浏览器上的“后退”按钮来更改路线,所以我不想在链接上使用事件来关闭模式。

在盖茨比,我可以使用gatsby-browser.js中的onRouteUpdate来检测路线更改,并且效果很好。但是我需要将事件传递给我的组件,这就是我遇到的困难。我简化了下面的代码以显示设置。

gatsby-browser.js:

import React from "react"
import Layout from "./src/components/layout"

export const wrapPageElement = ({ element, props }) => {
  return <Layout {...props}>{element}</Layout>
}

export const onRouteUpdate = () => {
  console.log("onRouteUpdate") // this works
}

layout.js:

import React from "react"

import Header from "./header"
import Footer from "./footer"

const Layout = ({ children }) => (
    <>
      <Header />
      <main>
        {children}
      </main>
      <Footer />
    </>
  )

export default Layout

header.js:

import React, { useState } from "react"

const Header = () => {
  const [isOpen, setIsOpen] = useState(null)
  const toggleState = ({ props }) => {
    let status
    if (props) status = props.status
    else status = !isOpen
    setIsOpen(status)
  }
  return (
    <header>
      <div>This is the header</div>
      <button onClick={toggleState}>Toggle Open/Close</button>
      <button onClick={toggleState({ status: false })}>This will always close</button>
      /* logic here uses isOpen state to determine display */
    </header>
  )
}

export default Header

2 个答案:

答案 0 :(得分:5)

我想出了一个解决自己问题的方法,所以我想我会分享。随时欢迎任何评论/改进。

首先,我们不需要在gatsby-broser.js中使用“ onRouteUpdate”,因此我们将其删除:

/* gatsby-browser.js */
import React from "react"
import Layout from "./src/components/layout"

export const wrapPageElement = ({ element, props }) => {
  return <Layout {...props}>{element}</Layout>
}

然后,在layout.js中确保将位置传递到标题:

/* layout.js */
import React from "react"

import Header from "./header"
import Footer from "./footer"

const Layout = ({ children, location }) => (
    <>
      <Header location={location} />
      <main>
        {children}
      </main>
      <Footer />
    </>
  )

export default Layout

最后,在header.js中,通过使用useRef钩子将位置存储在对header元素的引用中。 useEffect挂钩会在路线更改时触发,因此我们可以使用它进行比较:

/* header.js */
import React, { useState, useEffect, useRef } from "react"

const Header = () => {
  const [isOpen, setIsOpen] = useState(null)
  const myRef = useRef({
    location: null,
  })
  useEffect(() => {
    // set the location on initial load
    if (!myRef.current.location) myRef.current.location = location
    // then make sure dialog is closed on route change
    else if (myRef.current.location !== location) {
      if (isOpen) toggleState({ status: false })
      myRef.current.location = location
    }
  })
  const toggleState = ({ props }) => {
    let status
    if (props) status = props.status
    else status = !isOpen
    setIsOpen(status)
  }
  return (
    <header ref={myRef}>
      <div>This is the header</div>
      <button onClick={toggleState}>Toggle Open/Close</button>
      <button onClick={toggleState({ status: false })}>This will always close</button>
    </header>
  )
}

export default Header

希望这可以帮助任何寻求类似功能的人。

答案 1 :(得分:3)

解决该问题的首选方法是使用盖茨比使用的@reach/router中未记录的globalHistory

useEffect(() => {
  return globalHistory.listen(({ action }) => {
    if (action === 'PUSH') setIsOpen(false)
  })
}, [setIsOpen])

现在,无论何时切换路线,上述效果都会触发。

Source