在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
答案 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])
现在,无论何时切换路线,上述效果都会触发。