简体   繁体   中英

ReactJS: Need to change card styling using onClick function

I know this will need a refactor later to separate things out into their own components, but I'm up against a time crunch at the moment and need to wire this up as is. I used array.map() to create card elements from a JSON object I'm using for testing purposes. I'm attempting to use an onClick function on a card <div> to save some identifiable information like 'offerid' into the component state and then check if the id in state matches the current card. If it matches, I want to add cardActive as the className on the div so that only that specific card changes color. I'm not sure how to do this. As it is now, all card stylings are updated no matter which card is selected. My React component and corresponding CSS are listed below. Any help would be hugely appreciated

React

import React, { Component } from 'react';
import Grid from '@material-ui/core/Grid';
import './Button.css';

class UsersList extends Component {
    constructor(){
        super();

        this.state = {
            cardActive: false,

            customers:
            [
                {
                    "CustomerId": "1",
                    "LastName": "Doe",
                    "FirstName": "Jane",
                    "Address": {
                      "Address1": "1811 Chestnut Street",
                      "Address2": null,
                      "City": "Philadelphia",
                      "State": "Pennsylvania",
                      "Zip": "19103"
                    },
                    "Offers": [
                      {
                        "OfferId": "Offer1",
                        "Name": "Offer 1",
                        "Products": [
                          {
                            "ProductId": 1,
                            "ProductName": "Stuff"
                          },
                          {
                            "ProductId": 2,
                            "ProductName": "More stuff"
                          }
                        ],
                        "Price": "$1"
                      },
                      {
                        "OfferId": "Offer2",
                        "Name": "Offer 2",
                        "Price": "$2",
                        "Products": [
                          {
                            "ProductId": 3,
                            "ProductName": "A lot of stuff"
                          },
                          {
                            "ProductId": 4,
                            "ProductName": "And then there was stuff"
                          }
                        ]
                      },
                      {
                        "OfferId": "Offer3",
                        "Name": "Offer 3",
                        "Price": "$3",
                        "Products": [
                          {
                            "ProductId": 5,
                            "ProductName": "Good grief would you look at all this stuff"
                          },
                          {
                            "ProductId": 5,
                            "ProductName": "What a great deal for stuff"
                          }
                        ]
                      }
                    ]
                  }
              ]
        }
    }

    selectCard(){
        this.setState({ cardActive: !this.state.cardActive })
    }


    render (){
        let card_class = this.state.cardActive ? "cardActive" : "card";
        return (
            <div>
                {this.state.customers.map((customer, index) => {
                    return  <div key={index + customer.CustomerId}>
                                <h2>Customer</h2>
                                <hr></hr>
                                    <h3 >Name: {customer.LastName}, {customer.FirstName}</h3>
                                    <h3 >Customer ID: {customer.CustomerId}</h3>
                                    <h3 >
                                    Address: 
                                    <br></br>
                                    {customer.Address.Address1}
                                    <br></br>
                                    {customer.Address.City}, {customer.Address.State} {customer.Address.Zip} 
                                    </h3>
                                    <br></br>
                                    <h2>Available Offers</h2>
                                    <Grid container spacing={24} justify="center"> 
                                    {customer.Offers.map((Offer,index) => {
                                        return <div key={index + Offer.OfferId} onClick={this.selectCard.bind(this)}>
                                                <Grid item xs={12}>
                                                <div className="card" class={card_class}>
                                                    <div className="container">
                                                        <h5><b>{Offer.OfferId}</b></h5> 
                                                        <h2>{Offer.Name}</h2>
                                                        {Offer.Products.map((Product, index) => {
                                                            return <div key={index + Product.ProductId}>
                                                                    <p>+ {Product.ProductName}</p>
                                                                  </div>

                                                        })}
                                                        <h3>{Offer.Price}</h3> 
                                                    </div>
                                                </div>
                                                </Grid>
                                            </div>
                                    })}

                                    </Grid>

                            </div>

                })}
                <button className="navbuttonSelected">Submit</button>
            </div>
        )
    }
}

export default UsersList

CSS

  .card {
    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
    transition: 0.3s;
    border-radius: 5px; /* 5px rounded corners */
    margin-left: 70px;
    margin-right: 70px;
    margin-bottom: 5%;
    cursor: pointer;
  }

  .cardActive {
    box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
    transition: 0.01s;
    border-radius: 5px; /* 5px rounded corners */
    margin-left: 70px;
    margin-right: 70px;
    margin-bottom: 5%;
    background: #0c72c5 !important;
    color: white !important;
    cursor: pointer;
  }

  .cardActive:hover {
    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
  }

  .card:hover {
    box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
  }

Set id of selected card:

selectCard(offerId) {   
  this.setState({ cardActive: offerId });
}

change how onClick is called and apply specific class when Offer.OfferId === this.state.cardActive

return (  
 <div
   key={index + Offer.OfferId}
   onClick={() => this.selectCard(Offer.OfferId)}
 >
   <Grid item xs={12}>
     <div
      className={Offer.OfferId === this.state.cardActive ? "cardActive" : "card"}>

Working example: https://codesandbox.io/s/mjryv01528

I can suggest two approaches to fix your problem.

Approach 1:

  • In your selectCard method, in addition to cardActive, store the Id of card into state.

  • In your render method, in map function, consider Id(saved in state ) also to either apply your cardActive class.

  • With this approach, you will not be able to select multiple cards at a time. Only one card will be having cardActive class and others will not.
  • Also, you need to make sure you set Id to state only when card is selected. Not when its deselcted.

Approach2:

  • In your offers object of customer, in addition to having all the existing fields, add a property isActive and use this in your map method to either set cardActive class or the normal one.
  • Whenever a card is selected, update isActive property of the selected card (you can pass the offerObject to selectCard method and update isActive property in it or you can pass a unique identifier and use your customers object and update isActive property of selected object.
  • With this approach, you can have multiple cards selected as you are maintaining isActive at each customer level rather than one cardActive variable.
...
constructor(){
    super();

    this.state = {
        cardActive: "",

        customers: [...]
    }
    this.selectCard = this.selectCard.bind(this);
    this.getCardClass = this.getCardClass.bind(this);
}

selectCard(offerId){
    this.setState({ cardActive: offerId })
}

getCardClass(offerId) {
    const { cardActive } = this.state;
    return offerId === cardActive ? 'cardActive' : 'card';
}

render() {
...
    customer.Offers.map((Offer,index) => {
        return <div key={index + Offer.OfferId} onClick={() => this.selectCard(Offer.OfferId)}>
            <div item xs={12}>
            <div className="card" class={this.getCardClass(Offer.OfferId)}>
                <div className="container">
                    <h5><b>{Offer.OfferId}</b></h5> 
                    <h2>{Offer.Name}</h2>
                    {Offer.Products.map((Product, index) => {
                        return <div key={index + Product.ProductId}>
                                <p>+ {Product.ProductName}</p>
                              </div>

                    })}
                    <h3>{Offer.Price}</h3> 
                </div>
            </div>
            </div>
        </div>
    })
...

}

Here the selected card is stored in the state instead of just the it being true or false . The selectCard helper method sets the value of cardActive while getCardClass determines the selected card's class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM