javascript - 使用 useState 和 onClick 与 React 更改背景图像

标签 javascript css reactjs background-image use-state

我一直在兜圈子,试图让这项工作成功。我的目标是在点击链接后显示一个背景图像。我的导航上的每个链接在单击时都有自己的图像。我遇到的第一个错误是重新渲染次数过多。我对代码进行了更多操作,错误消失了,但是图像仍然没有在单击时呈现。 任何可以提供的帮助将不胜感激,我对 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})`,

navLinksarray ,所以它没有 bgI属性(property)。数组的各个元素都是带有 bgI 的对象。属性。

下一个令人困惑的事情是 addBgIHandler功能。该函数调用setNavLinksBackgroundImage并将状态更新为每个链接都有其 bgI 的图像。 。然后它会经历一堆 if/else案例并返回一个单个 bgI 。但这个返回值从未在任何地方使用过。

解决方案

一般来说,您希望以所需的状态存储最少量的信息。如果某些东西永远不会改变,那么它就不需要处于状态。我们需要知道的唯一变化信息是点击了哪个链接?

我会保留你的 navLinks在组件外部定义的数组,但将其更改为包含 bgI对于每个图像。我们想知道bgI对于每个链接,但这并不意味着我们将显示 bgI .

我们将使用状态来告诉我们哪个链接(如果有)是事件链接。我正在使用 id但这并不重要。您可以使用 titlepath相反。

// Should the initial state be home? Or no active link? That's up to you.
const [activeLinkId, setActiveLinkId] = useState(0);

如果链接是事件链接,我们只会在链接上显示背景图像。而不是常数css我们需要将其设为一个函数,以便每个链接的它都可以不同。我们将从 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 Prop 。

style={createCss(id, bgI)}

我们的新addBgIHandler非常简单,我们可以内联定义它。单击此链接时,我们设置 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;

react 路由器

我已经完全按照您提出的问题回答了这个问题,即“单击链接时如何在链接上显示图像?”但我看到 Link组件是从 react-router-dom 导入的所以我认为你没有问正确的问题。问题应该是“如何显示当前页面链接的图像?”该问题有一个完全不使用状态的不同答案。

您可以替换您的Link组件 NavLink .

A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL.

这不就是我们想要的吗?与NavLink我们可以放弃useStateonClickcreateCss 。相反,我们将背景样式设置为 activeStyle prop,仅当链接处于事件状态时才会应用这些样式。

{navLinks.map(({ title, path, bgI }) => (
  <NavLink
    to={path}
    key={title}
    className={classes.linkText}
    activeStyle={{
      backgroundImage: `url(${bgI})`,
      backgroundSize: "cover",
      backgroundRepeat: "no-repeat"
    }}
  >

关于javascript - 使用 useState 和 onClick 与 React 更改背景图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66736198/

相关文章:

javascript - 需要将值从 javascript 传递到 Spring mvc Controller

javascript - 使用 JQuery 每隔一定数量的 child 插入 HTML 元素

html - 不同元素的悬停效果不起作用

javascript - 侧边栏选项卡和滑动条在切换时不同步

html - 需要使用列表编号创建标题

javascript - 未处理的拒绝(类型错误): Cannot assign to read only property 'color' of object '#<Object>'

reactjs - axios嵌套请求函数返回

javascript - 当父 div 为 1000% 时,如何找到 li 的长度?

javascript - 在 Javascript 中将 Array 对象转换为 Json

javascript - 上下文映射函数中的 jsx-no-bind