【问题标题】:allow component to detect route change in Gatsby允许组件检测 Gatsby 中的路由变化
【发布时间】:2020-07-31 02:47:49
【问题描述】:

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

在 Gatsby 中,我可以使用 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

【问题讨论】:

    标签: reactjs gatsby


    【解决方案1】:

    解决此问题的首选方法是使用来自 @reach/router 的未记录的 globalHistory,Gatsby 使用它。

    import { globalHistory } from '@reach/router'
    
    useEffect(() => {
      return globalHistory.listen(({ action }) => {
        if (action === 'PUSH') setIsOpen(false)
      })
    }, [setIsOpen])
    

    现在每当你切换路线时,都会触发上述效果。

    Source.

    【讨论】:

    • 谢谢!像魅力一样工作。我找到的最简单的方法!!
    【解决方案2】:

    我想出了一个解决我自己问题的方法,所以我想我会分享。任何 cmets/改进总是受欢迎的。

    首先,我们不需要在 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
    

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

    【讨论】:

      猜你喜欢
      • 2017-02-18
      • 2017-12-27
      • 2021-06-16
      • 2020-06-11
      • 2017-01-01
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      • 2019-11-27
      相关资源
      最近更新 更多