使用 useState 和 onClick 与 React 更改背景图像
Changing background image using useState and onClick with React
我一直在兜圈子,试图让这项工作成功。我的目标是让背景图像在单击后显示在 link 后面。我的导航中的每个 Link 在单击时都有自己的图像。我遇到的第一个错误是重新渲染太多。我对代码进行了更多操作,错误消失了,但是图像仍然没有呈现 onClick。
可以提供的任何帮助将不胜感激,我对 React 还是很陌生,很想了解我做错了什么。谢谢!
import { Link } from 'react-router-dom'
import { useState } from 'react'
import Logo from '../../assets/images/Platform-Logo.svg'
import artistPaintBackground from '../../assets/images/Navigation-Artists-SVG.svg'
import eventsPaintBackground from '../../assets/images/Navigation-Events.svg'
import contactUsPaintBackground from '../../assets/images/Navigation-Contact-Us.png'
import homePaintBackground from '../../assets/images/Navigation-Home.png'
const navLinks = [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: '',
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: '',
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: '',
},
{
id: 3,
title: `CONTACT US`,
path: `/contactUs`,
bgI: '',
},
]
const Nav = (props) => {
const [navLinksBackgroundImage, setNavLinksBackgroundImage] = useState({
navLinks: [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: '',
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: '',
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: '',
},
{
id: 3,
title: 'CONTACT US',
path: `/contactUs`,
bgI: '',
},
],
})
const css = {
backgroundImage: `url(${navLinks.bgI})`,
width: '20%',
height: '100%',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
}
const addBgIHandler = () => {
setNavLinksBackgroundImage({
navLinks: [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: ` ${homePaintBackground}`,
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: `${eventsPaintBackground}`,
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: `${artistPaintBackground}`,
},
{
id: 3,
title: 'CONTACT US',
path: `/contactUs`,
bgI: `${contactUsPaintBackground}`,
},
],
})
if (navLinks.id === 0) {
return `${homePaintBackground}`
} else if (navLinks.id === 1) {
return `${eventsPaintBackground}`
} else if (navLinks.id === 2) {
return `${artistPaintBackground}`
} else if (navLinks.id === 3) {
return `${contactUsPaintBackground}`
} else {
return null
}
}
return (
<div>
<AppBar position='static' className={classes.navBar}>
<Toolbar>
<Container maxWidth='xl' className={classes.navDisplayFlex}>
<Link to='/'>
<img className={classes.imageLogo} src={Logo} />
</Link>
<Hidden smDown>
<ThemeProvider theme={theme}>
<List
component='nav'
aria-labelledby='main-navigation'
className={classes.navDisplayFlex}
>
{navLinks.map(({ title, path, bgI }) => (
<Link
active={bgI}
to={path}
key={title}
value={bgI}
className={classes.linkText}
onClick={addBgIHandler}
style={css}
>
<ListItem disableGutters={true}>
<ListItemText primary={title} />
</ListItem>
</Link>
))}
</List>
</ThemeProvider>
</Hidden>
<Hidden mdUp>
<Dropdown navLinks={navLinks} />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</div>
)
}
export default Nav
问题
这段代码中有一些错误。我看到的第一个是在这一行:
backgroundImage: `url(${navLinks.bgI})`,
navLinks
是一个 array
,因此它没有 bgI
属性。数组的各个元素是具有 bgI
属性.
的对象
下一个令人困惑的事情是 addBgIHandler
函数。此函数调用 setNavLinksBackgroundImage
并将状态更新为 每个 link 都有其 bgI
的图像。然后它经历了一堆 if
/else
个案例和 returns 个 单个 bgI
。但是这个返回值从来没有在任何地方使用过。
解决方案
一般来说,您希望以所需的状态存储最少量的信息。如果某件事永远不会改变,那么它就不需要处于状态。我们在这里需要知道的唯一变化信息是 点击了哪个 link?
我将保留在组件外部定义的 navLinks
数组,但对其进行更改,使其包含每个图像的 bgI
。我们想知道每个link的bgI
,但这并不意味着我们会显示bgI
.
我们将使用一个状态来告诉我们哪个 link(如果有的话)是活跃的 link。我正在使用 id
但没关系。您可以改用 title
或 path
。
// Should the initial state be home? Or no active link? That's up to you.
const [activeLinkId, setActiveLinkId] = useState(0);
如果 link 处于活动状态 link,我们只会在 link 上显示背景图像。我们需要将它变成一个函数,而不是常量 css
,以便每个 link 都可以不同。我们将从 navLinks.map
内部调用这个函数,这样我们就可以传入我们需要的参数。我们将需要 id
以便将其与 activeLinkId
状态进行比较,并需要 bgI
来设置背景图像(如果它处于活动状态)。
我建议您将始终存在的样式 width: '20%', height: '100%',
移动到您的 classes
对象中,并只在此处处理背景图像。
const createCss = (id: number, bgI: string) => {
if (id === activeLinkId) {
return {
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
};
} else return {};
};
在我自己的代码中,我会使用 ternary operator 而不是 if
/else
,但我想保持其清晰易读。
在Link
中,我们调用了设置style
属性的函数。
style={createCss(id, bgI)}
我们的新 addBgIHandler
非常简单,我们可以内联定义它。单击此 link 时,我们将 activeLinkId
设置为此 id
。
onClick={() => setActiveLinkId(id)}
代码
const navLinks = [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: ` ${homePaintBackground}`
},
{
id: 1,
title: "EVENTS",
path: "/events",
bgI: `${eventsPaintBackground}`
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: `${artistPaintBackground}`
},
{
id: 3,
title: "CONTACT US",
path: `/contactUs`,
bgI: `${contactUsPaintBackground}`
}
];
const Nav = (props) => {
// Should the initial state be home? Or no active link? That's up to you.
const [activeLinkId, setActiveLinkId] = useState(0);
const createCss = (id: number, bgI: string) => {
if (id === activeLinkId) {
return {
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
};
} else return {};
};
return (
<div>
<AppBar position="static" className={classes.navBar}>
<Toolbar>
<Container maxWidth="xl" className={classes.navDisplayFlex}>
<Link to="/">
<img className={classes.imageLogo} src={Logo} />
</Link>
<Hidden smDown>
<ThemeProvider theme={theme}>
<List
component="nav"
aria-labelledby="main-navigation"
className={classes.navDisplayFlex}
>
{navLinks.map(({ title, path, bgI, id }) => (
<Link
to={path}
key={title}
className={classes.linkText}
onClick={() => setActiveLinkId(id)}
style={createCss(id, bgI)}
>
<ListItem disableGutters={true}>
<ListItemText primary={title} />
</ListItem>
</Link>
))}
</List>
</ThemeProvider>
</Hidden>
<Hidden mdUp>
<Dropdown navLinks={navLinks} />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</div>
);
};
export default Nav;
反应路由器
我已经完全按照您提出的问题回答了问题,即 “单击 link 时如何显示图像?”但是我看到 Link
组件是从 react-router-dom
导入的,所以我认为您没有问正确的问题。问题应该是 “如何显示当前页面的图像 link?” 这个问题有一个完全不使用状态的不同答案。
您可以将 Link
组件替换为 NavLink
。
A special version of the <Link>
that will add styling attributes to the rendered element when it matches the current URL.
这不就是我们想要的吗?使用 NavLink
我们可以放弃 useState
和 onClick
以及 createCss
。相反,我们将背景样式设置为 activeStyle
属性,它只会在 link 处于活动状态时应用这些样式。
{navLinks.map(({ title, path, bgI }) => (
<NavLink
to={path}
key={title}
className={classes.linkText}
activeStyle={{
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
}}
>
我一直在兜圈子,试图让这项工作成功。我的目标是让背景图像在单击后显示在 link 后面。我的导航中的每个 Link 在单击时都有自己的图像。我遇到的第一个错误是重新渲染太多。我对代码进行了更多操作,错误消失了,但是图像仍然没有呈现 onClick。 可以提供的任何帮助将不胜感激,我对 React 还是很陌生,很想了解我做错了什么。谢谢!
import { Link } from 'react-router-dom'
import { useState } from 'react'
import Logo from '../../assets/images/Platform-Logo.svg'
import artistPaintBackground from '../../assets/images/Navigation-Artists-SVG.svg'
import eventsPaintBackground from '../../assets/images/Navigation-Events.svg'
import contactUsPaintBackground from '../../assets/images/Navigation-Contact-Us.png'
import homePaintBackground from '../../assets/images/Navigation-Home.png'
const navLinks = [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: '',
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: '',
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: '',
},
{
id: 3,
title: `CONTACT US`,
path: `/contactUs`,
bgI: '',
},
]
const Nav = (props) => {
const [navLinksBackgroundImage, setNavLinksBackgroundImage] = useState({
navLinks: [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: '',
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: '',
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: '',
},
{
id: 3,
title: 'CONTACT US',
path: `/contactUs`,
bgI: '',
},
],
})
const css = {
backgroundImage: `url(${navLinks.bgI})`,
width: '20%',
height: '100%',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
}
const addBgIHandler = () => {
setNavLinksBackgroundImage({
navLinks: [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: ` ${homePaintBackground}`,
},
{
id: 1,
title: 'EVENTS',
path: '/events',
bgI: `${eventsPaintBackground}`,
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: `${artistPaintBackground}`,
},
{
id: 3,
title: 'CONTACT US',
path: `/contactUs`,
bgI: `${contactUsPaintBackground}`,
},
],
})
if (navLinks.id === 0) {
return `${homePaintBackground}`
} else if (navLinks.id === 1) {
return `${eventsPaintBackground}`
} else if (navLinks.id === 2) {
return `${artistPaintBackground}`
} else if (navLinks.id === 3) {
return `${contactUsPaintBackground}`
} else {
return null
}
}
return (
<div>
<AppBar position='static' className={classes.navBar}>
<Toolbar>
<Container maxWidth='xl' className={classes.navDisplayFlex}>
<Link to='/'>
<img className={classes.imageLogo} src={Logo} />
</Link>
<Hidden smDown>
<ThemeProvider theme={theme}>
<List
component='nav'
aria-labelledby='main-navigation'
className={classes.navDisplayFlex}
>
{navLinks.map(({ title, path, bgI }) => (
<Link
active={bgI}
to={path}
key={title}
value={bgI}
className={classes.linkText}
onClick={addBgIHandler}
style={css}
>
<ListItem disableGutters={true}>
<ListItemText primary={title} />
</ListItem>
</Link>
))}
</List>
</ThemeProvider>
</Hidden>
<Hidden mdUp>
<Dropdown navLinks={navLinks} />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</div>
)
}
export default Nav
问题
这段代码中有一些错误。我看到的第一个是在这一行:
backgroundImage: `url(${navLinks.bgI})`,
navLinks
是一个 array
,因此它没有 bgI
属性。数组的各个元素是具有 bgI
属性.
下一个令人困惑的事情是 addBgIHandler
函数。此函数调用 setNavLinksBackgroundImage
并将状态更新为 每个 link 都有其 bgI
的图像。然后它经历了一堆 if
/else
个案例和 returns 个 单个 bgI
。但是这个返回值从来没有在任何地方使用过。
解决方案
一般来说,您希望以所需的状态存储最少量的信息。如果某件事永远不会改变,那么它就不需要处于状态。我们在这里需要知道的唯一变化信息是 点击了哪个 link?
我将保留在组件外部定义的 navLinks
数组,但对其进行更改,使其包含每个图像的 bgI
。我们想知道每个link的bgI
,但这并不意味着我们会显示bgI
.
我们将使用一个状态来告诉我们哪个 link(如果有的话)是活跃的 link。我正在使用 id
但没关系。您可以改用 title
或 path
。
// Should the initial state be home? Or no active link? That's up to you.
const [activeLinkId, setActiveLinkId] = useState(0);
如果 link 处于活动状态 link,我们只会在 link 上显示背景图像。我们需要将它变成一个函数,而不是常量 css
,以便每个 link 都可以不同。我们将从 navLinks.map
内部调用这个函数,这样我们就可以传入我们需要的参数。我们将需要 id
以便将其与 activeLinkId
状态进行比较,并需要 bgI
来设置背景图像(如果它处于活动状态)。
我建议您将始终存在的样式 width: '20%', height: '100%',
移动到您的 classes
对象中,并只在此处处理背景图像。
const createCss = (id: number, bgI: string) => {
if (id === activeLinkId) {
return {
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
};
} else return {};
};
在我自己的代码中,我会使用 ternary operator 而不是 if
/else
,但我想保持其清晰易读。
在Link
中,我们调用了设置style
属性的函数。
style={createCss(id, bgI)}
我们的新 addBgIHandler
非常简单,我们可以内联定义它。单击此 link 时,我们将 activeLinkId
设置为此 id
。
onClick={() => setActiveLinkId(id)}
代码
const navLinks = [
{
id: 0,
title: `HOME`,
path: `/`,
bgI: ` ${homePaintBackground}`
},
{
id: 1,
title: "EVENTS",
path: "/events",
bgI: `${eventsPaintBackground}`
},
{
id: 2,
title: `ARTISTS`,
path: `/artists`,
bgI: `${artistPaintBackground}`
},
{
id: 3,
title: "CONTACT US",
path: `/contactUs`,
bgI: `${contactUsPaintBackground}`
}
];
const Nav = (props) => {
// Should the initial state be home? Or no active link? That's up to you.
const [activeLinkId, setActiveLinkId] = useState(0);
const createCss = (id: number, bgI: string) => {
if (id === activeLinkId) {
return {
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
};
} else return {};
};
return (
<div>
<AppBar position="static" className={classes.navBar}>
<Toolbar>
<Container maxWidth="xl" className={classes.navDisplayFlex}>
<Link to="/">
<img className={classes.imageLogo} src={Logo} />
</Link>
<Hidden smDown>
<ThemeProvider theme={theme}>
<List
component="nav"
aria-labelledby="main-navigation"
className={classes.navDisplayFlex}
>
{navLinks.map(({ title, path, bgI, id }) => (
<Link
to={path}
key={title}
className={classes.linkText}
onClick={() => setActiveLinkId(id)}
style={createCss(id, bgI)}
>
<ListItem disableGutters={true}>
<ListItemText primary={title} />
</ListItem>
</Link>
))}
</List>
</ThemeProvider>
</Hidden>
<Hidden mdUp>
<Dropdown navLinks={navLinks} />
</Hidden>
</Container>
</Toolbar>
</AppBar>
</div>
);
};
export default Nav;
反应路由器
我已经完全按照您提出的问题回答了问题,即 “单击 link 时如何显示图像?”但是我看到 Link
组件是从 react-router-dom
导入的,所以我认为您没有问正确的问题。问题应该是 “如何显示当前页面的图像 link?” 这个问题有一个完全不使用状态的不同答案。
您可以将 Link
组件替换为 NavLink
。
A special version of the
<Link>
that will add styling attributes to the rendered element when it matches the current URL.
这不就是我们想要的吗?使用 NavLink
我们可以放弃 useState
和 onClick
以及 createCss
。相反,我们将背景样式设置为 activeStyle
属性,它只会在 link 处于活动状态时应用这些样式。
{navLinks.map(({ title, path, bgI }) => (
<NavLink
to={path}
key={title}
className={classes.linkText}
activeStyle={{
backgroundImage: `url(${bgI})`,
backgroundSize: "cover",
backgroundRepeat: "no-repeat"
}}
>