使用useState和onClick与React更改背景图像 [英] Changing background image using useState and onClick with React

查看:68
本文介绍了使用useState和onClick与React更改背景图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力使这项工作顺利进行.我的目标是要在单击链接后在链接后面显示背景图像.单击时,导航上的每个链接都有其自己的图像.我遇到的第一个错误是重新渲染过多.我更多地操作了代码,错误消失了,但是图像仍然无法呈现onClick.可以提供的任何帮助将不胜感激,我对React还是一个新手,很想了解我做错了什么.谢谢!

I have been going in circles trying to make this work. My goal is to have a background image that displays behind a link after it is clicked. Each Link on my Navigation has its own image for when clicked. The first error I was experiencing was too many re-renders. I manipulated the code more, the error went away however the images are still not rendering onClick. Any help that can be provided would be greatly appreciated, I am still pretty new to React and would love to understand what I am doing wrong. Thank you!

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

推荐答案

问题

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

Issues

There are a few errors in this code. The first one I see is on this line:

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

navLinks 是一个 array ,因此它没有 bgI 属性.数组的各个元素是具有 bgI 属性的对象.

navLinks is an array, so it doesn't have a bgI property. The individual elements of the array are objects with a bgI property.

接下来令人困惑的是 addBgIHandler 函数.此函数调用 setNavLinksBackgroundImage 并将状态更新为每个链接具有其 bgI 的图像的状态.然后,它会处理一堆 if / else 个案例,并返回一个单个 bgI .但是此返回值从未在任何地方使用.

The next confusing thing is the addBgIHandler function. This function calls setNavLinksBackgroundImage and updates the state to one where every link has an image for its bgI. It then goes through a bunch of if/else cases and returns a single bgI. But this returned value is never used anywhere.

通常,您希望以所需的状态存储最少的信息.如果某些事情永远不会改变,那么它就不必处于状态.我们唯一需要更改的信息是单击了哪个链接?

In general you want to store the minimal amount of information in state that you need. If something never changes then it doesn't need to be in state. The only changing information that we need to know here is which link was clicked?

我将在组件外部定义您的 navLinks 数组,但要对其进行更改,以使其每个图像都包含 bgI .我们想知道每个链接的 bgI ,但这并不意味着我们将显示 bgI .

I'm going to keep your navLinks array defined outside of the component, but change it so that it includes the bgI for every image. We want to know the bgI for each link, but that doesn't mean we will show the bgI.

我们将使用状态来告诉我们哪个链接(如果有)是活动链接.我正在使用 id ,但这没关系.您可以改用 title path .

We will use a state to tell us which link (if any) is the active link. I am using the id but it doesn't matter. You could use the title or the path instead.

// 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 来设置背景图像(如果处于活动状态).

We will only show the background image on a link if it is the active link. Instead of a constant css we will need to make it a function so that it can be different for each link. We are going to call this function from inside navLinks.map, so we can pass in the arguments that we need. We will need the id in order to compare it to the activeLinkId state and the bgI to set the background image if it's active.

我建议您移动样式 width:'20%',height:'100%',这些样式始终存在于您的 classes 对象中,并且仅用于处理这里的背景图片.

I would recommend that you move the styles width: '20%', height: '100%', which are always present into your classes object and just handle the background image here.

const createCss = (id: number, bgI: string) => {
  if (id === activeLinkId) {
    return {
      backgroundImage: `url(${bgI})`,
      backgroundSize: "cover",
      backgroundRepeat: "no-repeat"
    };
  } else return {};
};

在我自己的代码中,我将使用三元运算符而不是 if / else ,但我想保持其清晰易读.

In my own code I would use a ternary operator instead of if/else but I want to keep this clear and readable.

Link 中,我们调用该函数来设置 style 道具.

In the Link, we call the function to set the style prop.

style={createCss(id, bgI)}

我们的新 addBgIHandler 非常简单,我们可以内联定义它.单击此链接后,我们将 activeLinkId 设置为此 id .

Our new addBgIHandler is so simple that we can define it inline. When this link is clicked, we set the activeLinkId to this 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 导入的,所以我认为您并不是在问正确的问题.问题应该是我如何显示当前页面链接的图像?" 该问题有一个完全不使用状态的答案.

React Router

I've answered the question exactly as you've presented it, which is "how do I show an image on a link when it's clicked?" But I see that the Link component is imported from react-router-dom so I think that you aren't asking the right question. The question should be "how do I show an image for the current page link?" That question has a different answer which doesn't use state at all.

您可以将 Link 组件替换为 NavLink .

You can replace your Link components with NavLink.

< Link> 的特殊版本,当其与当前URL匹配时,它将为呈现的元素添加样式属性.

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 道具,该道具仅在链接处于活动状态时才会应用这些样式.

Isn't that what we want? With NavLink we can ditch the useState and the onClick and the createCss. Instead we set our background styles to the activeStyle prop, which will only apply these styles when the link is active.

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

这篇关于使用useState和onClick与React更改背景图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆