0

I'm trying to create a button to change the css of a component (Card.js) on Click but I'm not able to do it since I have to use a map function for a Tab.

That's the code.

The component Card.js:

const Cardbox = styled.div`
...
`

const Cards = styled.div`
    width: 150px;
    height: 220px;
    background: url(${props => props.bg}) no-repeat top center;
`

const Name = styled.h3`
...
`

const Role = styled.p`
...
`

const Card = props => (
  <Link to={props.link}>
    <Cardbox>
      <Cards bg={require(`./../images/Cards/${props.title}_Card.jpg`)}/>
      <div>
        <Name>{props.title}</Name>
        <Role>{props.text}</Role>
      </div>
    </Cardbox>
  </Link>
)

export default Card

Tab.js component (I get data from a constant file):

    class Tabs extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                ...
            }
        }
        ...
        makeHeroCard = (hero, index) => (
            <Card
                title={hero.name}
                text={hero.type.join(', ')}
                image={
                    require(`./../images/Cards/${hero.name}_Card.jpg`)
                }
                link={hero.name}
                key={index}
            />
        )

        render() {
            ...
            return (
                <div>
                    <button>Click here and change css</button>
                    <AppBar>
                        <Tabs>
                            <Tab label="Item One"/>
                            <Tab label="Item Two"/>
                            <Tab label="Item Three"/>
                        </Tabs>
                    </AppBar>
                    {value === 0 && (
                        <TabContainer>
                            <div className="GroupScroll">
                                <div className="Group">
                                    {heroCards.map((hero, index) => this.makeHeroCard(hero, index))}
                                </div>
                            </div>
                        </TabContainer>
                    )}
                    {value === 1 && (
                        <TabContainer>
                            <div className="GroupScroll">
                                <div className="Group">
                                    {heroCards
                                        .filter(hero => hero.type.includes('TANK'))
                                        .map((hero, index) => this.makeHeroCard(hero, index))
                                    }
                                </div>
                            </div>
                        </TabContainer>
                    )}
                </div>
            )
        }
    }

    export default Tabs

What I'd like to do on clicking the button is: 1. change width,height and src of the img; 2. hide h1 and p.

How can I do it using {heroCards.map(...)}?

Thanks in advance.

remix23
  • 2,632
  • 2
  • 11
  • 21
AC1
  • 89
  • 1
  • 5
  • I'm wondering why cant the styles you need be embedded in a general stylesheet and all you do is add or remove classes on the element? – jidexl21 Sep 02 '19 at 10:52
  • Because it's not the styled-components way of doing things. Styled-components abstracts the classnames and the stylesheet for us. – remix23 Sep 02 '19 at 11:06

1 Answers1

1

The map doesn't change much, it's not clear which card you want to alter but it's as simple as giving a new prop to your cards.

    const Card = props => (
        <Link to={props.link}>
            <Cardbox>
                <Cards bg={require(`./../images/Cards/${props.title}_Card.jpg`)}/>
                {!props.hideNameAndRole && (
                <div>
                    <Name>{props.title}</Name>
                    <Role>{props.text}</Role>
                </div>
                )}
            </Cardbox>
        </Link>
     )

Also, try and avoid dynamically require during rendering: (inspired from this question)

    function importAll(r) {
        const heroCards = {};
        r.keys().map((item, index) => { 

          const key = item.replace(
              /\.\/\.\.\/images\/Cards\/(.+)_Card\.jpg/,
              '$1'
          );

          heroCards[key] = r(item);
        });
        return heroCards;
    }

    const heroCards = importAll(
        require.context(
            './../images/Cards',
            false,
            /\.(png|jpe?g|svg)$/
         )
    );

Then add a state to your Tabs component and change it on click on the button:

    state = {
        hideNameAndRole: false,
    }
    handleHideClick = () => this.setState(() => ({ hideNameAndRole: true })
    makeHeroCard = (hero, index) => (
        <Card
            title={hero.name}
            text={hero.type.join(', ')}
            image={heroCards[hero.name]}
            link={hero.name}
            key={index}
            hideNameAndRole={this.state.hideNameAndRole}
        />
    )
    ...
    render() {
        ...
        <button onClick={this.handleHideClick}>Click here and change css</button>
        ...
    }

You question mentionned css so instead of conditionally render Name and Role you could add an interpolation to their css:

    const Name = styled.h3`
        ...
        ${props => (props.hidden? 'visibility: hidden;' : '')}

    `

    const Role = styled.p`
        ...
        ${props => (props.hidden? 'visibility: hidden;' : '')}
    `

And active the interpolation through the same prop:

    const Card = props => (
        <Link to={props.link}>
            <Cardbox>
                <Cards bg={require(`./../images/Cards/${props.title}_Card.jpg`)}/>
                <div>
                    <Name hidden={props.hideNameAndRole}>{props.title}</Name>
                    <Role hidden={props.hideNameAndRole}>{props.text}</Role>
                </div>
            </Cardbox>
        </Link>
    )

remix23
  • 2,632
  • 2
  • 11
  • 21
  • Hi, really thanks! I'm now able to work on Card's width and height, but I made a mistake: the component is . So, how can I change the Card's bg image={require(`./../images/Cards/${hero.name}_Card.jpg`)}) on click? – AC1 Sep 02 '19 at 12:21
  • The same way... Can you explain precisely the change you want to do? – remix23 Sep 02 '19 at 13:04
  • If you can, I suggest that you import all the assets first instead of dynamically require during rendering. I'm updating my answer for that – remix23 Sep 02 '19 at 13:08
  • I was trying something like ${props => (props.reduce ? 'background: url(${props => props.bg2});' : '')}. is it the right way? – AC1 Sep 02 '19 at 18:32
  • Well, yes if `reduce` is the prop meant to change the background to `bg2` prop... You definitely can write this anyway – remix23 Sep 03 '19 at 08:25