【问题标题】:Changing background image using useState and onClick with React通过 React 使用 useState 和 onClick 更改背景图像
【发布时间】:2021-06-17 05:06:55
【问题描述】:

我一直在兜圈子试图使这项工作。我的目标是在单击链接后显示背景图像。我的导航上的每个链接在单击时都有自己的图像。我遇到的第一个错误是重新渲染太多。我对代码进行了更多操作,错误消失了,但是图像仍然没有呈现 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

【问题讨论】:

    标签: javascript css reactjs background-image use-state


    【解决方案1】:

    问题

    此代码中有一些错误。我看到的第一个是在这一行:

    backgroundImage: `url(${navLinks.bgI})`,
    

    navLinksarray,因此它没有 bgI 属性。数组的各个元素是具有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属性。

    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;
    

    反应路由器

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

    您可以用NavLink 替换您的Link 组件。

    &lt;Link&gt; 的特殊版本,当它与当前 URL 匹配时,它将向呈现的元素添加样式属性。

    这不是我们想要的吗?使用NavLink,我们可以放弃useStateonClickcreateCss。相反,我们将背景样式设置为 activeStyle 属性,它只会在链接处于活动状态时应用这些样式。

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

    【讨论】:

    • 非常感谢您的解释,这真的很有帮助。我会努力落实你的建议!!谢谢,谢谢!!
    • 我想尝试实施您的 NavLinks 建议,但是现在它始终显示主页背景图像,我在您的评论中看到您说初始状态是什么取决于我,但我无法弄清楚如何使其仅在链接处于活动状态时显示。感谢您的帮助。
    • 似乎其他人正在取消选择,但始终选择“Home”,因为所有路径都匹配“/”。你需要exact prop
    • 是的,准确添加到 NavLink
    • 非常感谢您带我完成这一切!你是最有帮助的!!将精确添加到 NavLink 效果很好!你是我的英雄! :)
    猜你喜欢
    • 2021-06-18
    • 2020-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-16
    相关资源
    最近更新 更多