需要帮助将 Gatsby 组件转换为 NextJS
Need help converting a Gatsby component to NextJS
请参阅下面的代码,因为我正在尝试将此 header 组件从 Gatsby 转换为 NextJS...我 运行 遇到的问题有两个方面:首先,您会注意到我有
import {Link, navigate} from 'gatsby'
我知道我可以只使用 'next/link' 中的 Link 而不是 Gatsby 中的 Link,但是从 gatsby 导航的替代方法是什么?
其次(也是更困难的),我在尝试使用来自@material-ui 的 makeStyles 挂钩时收到以下错误:
我完全不知道如何解决这个问题,我已经包含了下面的所有代码...请帮忙!!!
import React, { useState } from 'react'
import AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import Typography from '@material-ui/core/Typography'
import Button from '@material-ui/core/Button'
import IconButton from '@material-ui/core/IconButton'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import { makeStyles } from '@material-ui/core/styles'
import useMediaQuery from '@material-ui/core/useMediaQuery'
import Hidden from '@material-ui/core/Hidden'
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemText from '@material-ui/core/ListItemText'
import { Link, navigate } from 'gatsby'
import Link from 'next/link'
import menu from '../../images/menu.svg'
import account from '../../images/account-header.svg'
import cart from '../../images/cart.svg'
import search from '../../images/search.svg'
const useStyles = makeStyles(theme => ({
coloredIndicator: {
backgroundColor: 'lightpurple',
},
logoText: {
color: theme.palette.common.offBlack,
},
logoContainer: {
[theme.breakpoints.down('md')]: {
marginRight: 'auto',
},
},
tab: {
...theme.typography.body1,
fontWeight: 600,
},
tabs: {
marginLeft: 'auto',
marginRight: 'auto',
},
icon: {
height: '3rem',
width: '3rem',
},
drawer: {
backgroundColor: theme.palette.primary.main,
},
listItemText: {
color: theme.palette.common.white,
},
}))
export default function Header({ categories }) {
const classes = useStyles()
console.log('HEADER: ', categories)
const matchesMD = useMediaQuery(theme => theme.breakpoints.down('md'))
const [drawerOpen, setDrawerOpen] = useState(false)
// const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent)
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
const activeIndex = () => {
const found = routes.indexOf(
routes.filter(
({ node: { name, link } }) =>
(link || `/${name.toLowerCase()}`) === window.location.pathname
)[0]
)
return found === -1 ? false : found
}
const routes = [
...categories,
{ node: { name: 'Contact Us', strapiId: 'contact', link: '/contact' } },
]
const tabs = (
<Tabs
value={activeIndex()}
classes={{ indicator: classes.coloredIndicator, root: classes.tabs }}
>
{routes.map(route => (
<Tab
classes={{ root: classes.tab }}
component={Link}
to={route.node.link || `/${route.node.name.toLowerCase()}`}
label={route.node.name}
key={route.node.strapiId}
/>
))}
</Tabs>
)
const drawer = (
<SwipeableDrawer
open={drawerOpen}
onOpen={() => setDrawerOpen(true)}
onClose={() => setDrawerOpen(false)}
disableBackdropTransition={!iOS}
disableDiscovery={iOS}
classes={{ paper: classes.drawer }} // iOS only
>
<List disablePadding>
{routes.map((route, i) => (
<ListItem
selected={activeIndex() === i}
component={Link}
to={route.node.link || `/${route.node.name.toLowerCase()}`}
divider
button
key={route.node.strapiId}
>
<ListItemText
classes={{ primary: classes.listItemText }}
primary={route.node.name}
/>
</ListItem>
))}
</List>
</SwipeableDrawer>
)
const actions = [
{
icon: search,
alt: 'search',
visible: true,
onClick: () => console.log('search'),
},
{
icon: cart,
alt: 'cart',
visible: true,
link: '/cart',
},
{
icon: account,
alt: 'account',
visible: !matchesMD,
link: '/account',
},
{
icon: menu,
alt: 'menu',
visible: matchesMD,
onClick: () => setDrawerOpen(true),
},
]
return (
<AppBar color="transparent" elevation={0}>
<Toolbar>
<Button
component={Link}
to="/"
classes={{ root: classes.logoContainer }}
>
<Typography variant="h1">
<span className={classes.logoText}>VAR</span> X
</Typography>
</Button>
{matchesMD ? drawer : tabs}
{actions.map(action => {
if (action.visible) {
return (
<IconButton
onClick={action.onClick}
key={action.alt}
component={action.onClick ? undefined : Link}
to={action.onClick ? undefined : action.link}
>
<img
className={classes.icon}
src={action.icon}
alt={action.alt}
/>
</IconButton>
)
}
})}
</Toolbar>
</AppBar>
)
}
Gatsby 的替代 navigate
应该代表 Next's useRouter
,这不一样但是稍微调整一下你可以获得相同的功能。实例化为 const router = useRouter()
后,您可以使用:
router.push(url, as, options)
其中 url
代表您的动态导航路径。
关于您的第二个问题(破坏您的代码的问题):它与 Gatsby 或 Next 无关,而是与 React 无关,您正在破坏 rule of hooks 或者因为 React [=] 之间的版本不匹配26=]。提供更多信息或调试日志。
如果您将 useStyles
移动到组件内部,问题应该会消失,但您需要稍微清理一下组件。
请参阅下面的代码,因为我正在尝试将此 header 组件从 Gatsby 转换为 NextJS...我 运行 遇到的问题有两个方面:首先,您会注意到我有
import {Link, navigate} from 'gatsby'
我知道我可以只使用 'next/link' 中的 Link 而不是 Gatsby 中的 Link,但是从 gatsby 导航的替代方法是什么?
其次(也是更困难的),我在尝试使用来自@material-ui 的 makeStyles 挂钩时收到以下错误:
import React, { useState } from 'react'
import AppBar from '@material-ui/core/AppBar'
import Toolbar from '@material-ui/core/Toolbar'
import Typography from '@material-ui/core/Typography'
import Button from '@material-ui/core/Button'
import IconButton from '@material-ui/core/IconButton'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import { makeStyles } from '@material-ui/core/styles'
import useMediaQuery from '@material-ui/core/useMediaQuery'
import Hidden from '@material-ui/core/Hidden'
import SwipeableDrawer from '@material-ui/core/SwipeableDrawer'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemText from '@material-ui/core/ListItemText'
import { Link, navigate } from 'gatsby'
import Link from 'next/link'
import menu from '../../images/menu.svg'
import account from '../../images/account-header.svg'
import cart from '../../images/cart.svg'
import search from '../../images/search.svg'
const useStyles = makeStyles(theme => ({
coloredIndicator: {
backgroundColor: 'lightpurple',
},
logoText: {
color: theme.palette.common.offBlack,
},
logoContainer: {
[theme.breakpoints.down('md')]: {
marginRight: 'auto',
},
},
tab: {
...theme.typography.body1,
fontWeight: 600,
},
tabs: {
marginLeft: 'auto',
marginRight: 'auto',
},
icon: {
height: '3rem',
width: '3rem',
},
drawer: {
backgroundColor: theme.palette.primary.main,
},
listItemText: {
color: theme.palette.common.white,
},
}))
export default function Header({ categories }) {
const classes = useStyles()
console.log('HEADER: ', categories)
const matchesMD = useMediaQuery(theme => theme.breakpoints.down('md'))
const [drawerOpen, setDrawerOpen] = useState(false)
// const iOS = process.browser && /iPad|iPhone|iPod/.test(navigator.userAgent)
const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent)
const activeIndex = () => {
const found = routes.indexOf(
routes.filter(
({ node: { name, link } }) =>
(link || `/${name.toLowerCase()}`) === window.location.pathname
)[0]
)
return found === -1 ? false : found
}
const routes = [
...categories,
{ node: { name: 'Contact Us', strapiId: 'contact', link: '/contact' } },
]
const tabs = (
<Tabs
value={activeIndex()}
classes={{ indicator: classes.coloredIndicator, root: classes.tabs }}
>
{routes.map(route => (
<Tab
classes={{ root: classes.tab }}
component={Link}
to={route.node.link || `/${route.node.name.toLowerCase()}`}
label={route.node.name}
key={route.node.strapiId}
/>
))}
</Tabs>
)
const drawer = (
<SwipeableDrawer
open={drawerOpen}
onOpen={() => setDrawerOpen(true)}
onClose={() => setDrawerOpen(false)}
disableBackdropTransition={!iOS}
disableDiscovery={iOS}
classes={{ paper: classes.drawer }} // iOS only
>
<List disablePadding>
{routes.map((route, i) => (
<ListItem
selected={activeIndex() === i}
component={Link}
to={route.node.link || `/${route.node.name.toLowerCase()}`}
divider
button
key={route.node.strapiId}
>
<ListItemText
classes={{ primary: classes.listItemText }}
primary={route.node.name}
/>
</ListItem>
))}
</List>
</SwipeableDrawer>
)
const actions = [
{
icon: search,
alt: 'search',
visible: true,
onClick: () => console.log('search'),
},
{
icon: cart,
alt: 'cart',
visible: true,
link: '/cart',
},
{
icon: account,
alt: 'account',
visible: !matchesMD,
link: '/account',
},
{
icon: menu,
alt: 'menu',
visible: matchesMD,
onClick: () => setDrawerOpen(true),
},
]
return (
<AppBar color="transparent" elevation={0}>
<Toolbar>
<Button
component={Link}
to="/"
classes={{ root: classes.logoContainer }}
>
<Typography variant="h1">
<span className={classes.logoText}>VAR</span> X
</Typography>
</Button>
{matchesMD ? drawer : tabs}
{actions.map(action => {
if (action.visible) {
return (
<IconButton
onClick={action.onClick}
key={action.alt}
component={action.onClick ? undefined : Link}
to={action.onClick ? undefined : action.link}
>
<img
className={classes.icon}
src={action.icon}
alt={action.alt}
/>
</IconButton>
)
}
})}
</Toolbar>
</AppBar>
)
}
Gatsby 的替代 navigate
应该代表 Next's useRouter
,这不一样但是稍微调整一下你可以获得相同的功能。实例化为 const router = useRouter()
后,您可以使用:
router.push(url, as, options)
其中 url
代表您的动态导航路径。
关于您的第二个问题(破坏您的代码的问题):它与 Gatsby 或 Next 无关,而是与 React 无关,您正在破坏 rule of hooks 或者因为 React [=] 之间的版本不匹配26=]。提供更多信息或调试日志。
如果您将 useStyles
移动到组件内部,问题应该会消失,但您需要稍微清理一下组件。