MDB React Card Flipping


Topic: MDB React Card Flipping

kpickering asked 5 years ago

**Expected behavior** I am using props to load in multiple flipping cards, I am able to render the cards but when I go to update the state so the cards are able to flip individually it states that there is no state defined.

**Actual behavior** Page is unable to load as it stating there is no state defined and the function "handleFlipping" is also not defined.

**Resources (screenshots, code snippets etc.)**

Here is a link to the project without adding in the state feature so you able to see how it works.

https://agile-river-38619.herokuapp.com/meraki-oil

here is the code for the two pages --

Card Page: import React from "react"; import {MDBCard, MDBCol, MDBCardBody, MDBRotatingCard, MDBIcon} from "mdbreact"; import Ingredients from "../ingredients"; import ingredients from "../ingredients.json";

const IngredientsCard = props =>{ state = { ingredients, flipped1: false, flipped2: false, flipped3: false, flipped4: false, flipped5: false, flipped6: false, flipped7: false, flipped8: false, flipped9: false, flipped10: false, flipped11: false, flipped12: false, flipped13: false, flipped14: false, flipped15: false, flipped16: false, flipped17: false, flipped18: false, flipped19: false, flipped20: false, flipped21: false, flipped22: false, flipped23: false, flipped24: false, flipped25: false, flipped26: false, flipped27: false, flipped28: false, flipped29: false, flipped30: false, flipped31: false, flipped32: false, flipped33: false, flipped34: false, } handleFlipping = id => () => { const cardId = flipped${id}; this.setState({ [cardId]: !this.state[cardId] }); }

return(

        <div>
            <MDBCol style={{ width: "22rem", height: "18rem" }}>
      <MDBRotatingCard flipped={this.state.props.flipped} className="text-center h-100 w-100">
        <MDBCard className="face front">

          <MDBCardBody>
          <img className="card-img-top" src={Ingredients[props.image]} alt={props.name} />
            <h4 className="font-weight-bold mb-3">{props.name}</h4>
            <a href="#!" className="rotate-btn text-dark" data-card="card-4" onClick={this.handleFlipping(props.key)}>
              <MDBIcon icon="redo" /> Click here to learn more
            </a>
          </MDBCardBody>
        </MDBCard>
        <MDBCard className="face back" style={{ height: "300px" }}>
          <MDBCardBody>
            <h4 className="font-weight-bold">Benefits</h4>
            <hr />
            <p>
          {props.body} </p><hr />

            <a href="#!" className="rotate-btn text-dark" data-card="card-4" onClick={this.handleFlipping(props.key)}>
              <MDBIcon icon="undo" /> Click here to rotate back
            </a>
          </MDBCardBody>
        </MDBCard>
      </MDBRotatingCard>
    </MDBCol>
        </div>
)

}

export default IngredientsCard;

Loading into Carousel

import React, { Component } from "react"; import "../index.css"; import { MDBContainer } from "mdbreact"; import Carousel from "react-multi-carousel"; import ingredients from "../ingredients.json"; import IngredientsCard from "./IngredientsCard.js";

class IngredientsCarousel extends Component {

state = {
  ingredients,
    flipped1: false,
    flipped2: false,
    flipped3: false,
    flipped4: false,
    flipped5: false,
    flipped6: false,
    flipped7: false,
    flipped8: false,
    flipped9: false,
    flipped10: false,
    flipped11: false,
    flipped12: false,
    flipped13: false,
    flipped14: false,
    flipped15: false,
    flipped16: false,
    flipped17: false,
    flipped18: false,
    flipped19: false,
    flipped20: false,
    flipped21: false,
    flipped22: false,
    flipped23: false,
    flipped24: false,
    flipped25: false,
    flipped26: false,
    flipped27: false,
    flipped28: false,
    flipped29: false,
    flipped30: false,
    flipped31: false,
    flipped32: false,
    flipped33: false,
    flipped34: false,
  }

  handleFlipping = id => () => {
    const cardId = `flipped${id}`;
    this.setState({ [cardId]: !this.state[cardId] });
  }

render(){
    const responsive = {
        desktop: {
          breakpoint: { max: 3000, min: 1024 },
          items: 5,
          slidesToSlide: 1, // optional, default to 1.
        },
        tablet: {
          breakpoint: { max: 1024, min: 464 },
          items: 2,
          slidesToSlide: 2, // optional, default to 1.
        },
        mobile: {
          breakpoint: { max: 464, min: 0 },
          items: 1,
          slidesToSlide: 1, // optional, default to 1.
        },
      };
    return(
        <div>
           <MDBContainer>
                     <Carousel

swipeable={true} draggable={true} showDots={false} responsive={responsive} ssr={true} // means to render carousel on server-side. infinite={true} keyBoardControl={true} customTransition="all .5" transitionDuration={500} containerClass="carousel-container" removeArrowOnDeviceType={["tablet", "mobile"]} deviceType={this.props.deviceType} dotListClass="custom-dot-list-style" itemClass="carousel-item-padding-40-px"

    {this.state.ingredients.map(merakiIngredients =>(

    <IngredientsCard
    id={merakiIngredients.id}
    key={merakiIngredients.key}
    name={merakiIngredients.name}
    image={merakiIngredients.image}
    flipped={merakiIngredients.flipped}
    flipping={merakiIngredients.flipping}

    />

    ))}

; ) }

}

export default IngredientsCarousel;


Jakub Chmura staff premium answered 5 years ago

hi @kpickering,

First of all, you can't manage the state in functional component as you do in an IngredientsCard component.

Your code has many errors and it's hard for me to tell you what's wrong with it.

I tried to repair some of the errors. Check this code now and let me know that my solution works in your case.

First Component:

import React, { Component } from 'react';
import {
  MDBCard,
  MDBCol,
  MDBCardBody,
  MDBRotatingCard,
  MDBIcon
} from 'mdbreact';
import Ingredients from '../ingredients';
import ingredients from '../ingredients.json';

class IngredientsCard extends Component {
  state = {
    ingredients,
    flipped1: false,
    flipped2: false,
    flipped3: false,
    flipped4: false,
    flipped5: false,
    flipped6: false,
    flipped7: false,
    flipped8: false,
    flipped9: false,
    flipped10: false,
    flipped11: false,
    flipped12: false,
    flipped13: false,
    flipped14: false,
    flipped15: false,
    flipped16: false,
    flipped17: false,
    flipped18: false,
    flipped19: false,
    flipped20: false,
    flipped21: false,
    flipped22: false,
    flipped23: false,
    flipped24: false,
    flipped25: false,
    flipped26: false,
    flipped27: false,
    flipped28: false,
    flipped29: false,
    flipped30: false,
    flipped31: false,
    flipped32: false,
    flipped33: false,
    flipped34: false
  };

  handleFlipping = id => {
    const cardId = `flipped${id}`;
    this.setState({ [cardId]: !this.state[cardId] });
  };

  render() {
    const { name, image, key, body, flipped } = this.props;
    return (
      <div>
        <MDBCol style={{ width: '22rem', height: '18rem' }}>
          <MDBRotatingCard
            flipped={flipped}
            className='text-center h-100 w-100'
          >
            <MDBCard className='face front'>
              <MDBCardBody>
                <img
                  className='card-img-top'
                  src={Ingredients[image]}
                  alt={name}
                />
                <h4 className='font-weight-bold mb-3'>{name}</h4>
                <a
                  href='#!'
                  className='rotate-btn text-dark'
                  data-card='card-4'
                  onClick={this.handleFlipping(key)}
                >
                  <MDBIcon icon='redo' /> Click here to learn more
                </a>
              </MDBCardBody>
            </MDBCard>
            <MDBCard className='face back' style={{ height: '300px' }}>
              <MDBCardBody>
                <h4 className='font-weight-bold'>Benefits</h4>
                <hr />
                <p>{body} </p>
                <hr />

                <a
                  href='#!'
                  className='rotate-btn text-dark'
                  data-card='card-4'
                  onClick={this.handleFlipping(key)}
                >
                  <MDBIcon icon='undo' /> Click here to rotate back
                </a>
              </MDBCardBody>
            </MDBCard>
          </MDBRotatingCard>
        </MDBCol>
      </div>
    );
  }
}

export default IngredientsCard;

And the second component.

import React, { Component } from 'react';
import '../index.css';
import { MDBContainer } from 'mdbreact';
import Carousel from 'react-multi-carousel';
import ingredients from '../ingredients.json';
import IngredientsCard from './srap';

class IngredientsCarousel extends Component {
  state = {
    ingredients,
    flipped1: false,
    flipped2: false,
    flipped3: false,
    flipped4: false,
    flipped5: false,
    flipped6: false,
    flipped7: false,
    flipped8: false,
    flipped9: false,
    flipped10: false,
    flipped11: false,
    flipped12: false,
    flipped13: false,
    flipped14: false,
    flipped15: false,
    flipped16: false,
    flipped17: false,
    flipped18: false,
    flipped19: false,
    flipped20: false,
    flipped21: false,
    flipped22: false,
    flipped23: false,
    flipped24: false,
    flipped25: false,
    flipped26: false,
    flipped27: false,
    flipped28: false,
    flipped29: false,
    flipped30: false,
    flipped31: false,
    flipped32: false,
    flipped33: false,
    flipped34: false
  };

  handleFlipping = id => () => {
    const cardId = `flipped${id}`;
    this.setState({ [cardId]: !this.state[cardId] });
  };

  render() {
    const responsive = {
      desktop: {
        breakpoint: { max: 3000, min: 1024 },
        items: 5,
        slidesToSlide: 1 // optional, default to 1.
      },
      tablet: {
        breakpoint: { max: 1024, min: 464 },
        items: 2,
        slidesToSlide: 2 // optional, default to 1.
      },
      mobile: {
        breakpoint: { max: 464, min: 0 },
        items: 1,
        slidesToSlide: 1 // optional, default to 1.
      }
    };
    return (
      <div>
        <MDBContainer>
          <Carousel
            swipeable={true}
            draggable={true}
            showDots={false}
            responsive={responsive}
            ssr={true}
            // means to render carousel on server-side. infinite={true} keyBoardControl={true} customTransition="all .5" transitionDuration={500} containerClass="carousel-container" removeArrowOnDeviceType={["tablet", "mobile"]} deviceType={this.props.deviceType} dotListClass="custom-dot-list-style" itemClass="carousel-item-padding-40-px"
          >
            {this.state.ingredients.map(merakiIngredients => (
              <IngredientsCard
                id={merakiIngredients.id}
                key={merakiIngredients.key}
                name={merakiIngredients.name}
                image={merakiIngredients.image}
                flipped={merakiIngredients.flipped}
                flipping={merakiIngredients.flipping}
              />
            ))}
          </Carousel>
        </MDBContainer>
      </div>
    );
  }
}

export default IngredientsCarousel;

Best regards,

Kuba


kpickering answered 5 years ago

result

I have updated my code and this is the error message now.


Jakub Chmura staff premium answered 5 years ago

Hi @kpickering

Error from your screenshot means that your function has fallen into an infinite loop when you try to set your state by clicking the card. You need to check why this function is calling so many times.

I see that in your code is some errors. Look at your sample that you send me. You set the same state in your IngredientsCarousel component and IngredientsCard component. You need to decide on which component you want to manage your state. At your IngredientsCarousel component, you write there a handleFlipping method and you're not calling them.

If there is anything else I could do for you do not hesitate to ask me. I'll be happy to help you.

Best Regards,

Kuba


kpickering answered 5 years ago

Thank you for your help with this, I am still very new at this. I have solved the problem with the continuous loops by doing this:

onClick={() => this.handleFlipping(id)}

But now, all of the cards are flipped to the back without any information on it, I am clearly still missing something, and I would really like if you could point me in the direction I should be looking!

import React, { Component } from "react";
import {MDBCard, MDBCol, MDBCardBody, MDBRotatingCard, MDBIcon} from "mdbreact";
import Ingredients from "../ingredients";
import ingredients from "../ingredients.json";


class IngredientsCard extends Component {
 state = {
ingredients,
flipped1: false,
flipped2: false,
flipped3: false,
flipped4: false,
flipped5: false,
flipped6: false,
flipped7: false,
flipped8: false,
flipped9: false,
flipped10: false,
flipped11: false,
flipped12: false,
flipped13: false,
flipped14: false,
flipped15: false,
flipped16: false,
flipped17: false,
flipped18: false,
flipped19: false,
flipped20: false,
flipped21: false,
flipped22: false,
flipped23: false,
flipped24: false,
flipped25: false,
flipped26: false,
flipped27: false,
flipped28: false,
flipped29: false,
flipped30: false,
flipped31: false,
flipped32: false,
flipped33: false,
flipped34: false

};

handleFlipping = id => {
const cardId = `flipped${id}`;
this.setState({ [cardId]: !this.state[cardId] });

};

 render() {
   const { name, image, id, body, flipped } = this.props;
   return (
  <div>
    <MDBCol style={{ width: '22rem', height: '18rem' }}>
      <MDBRotatingCard
        flipped={flipped}
        className='text-center h-100 w-100'
      >
        <MDBCard className='face front'>
          <MDBCardBody>
            <img
              className='card-img-top'
              src={Ingredients[image]}
              alt={name}
            />
            <h4 className='font-weight-bold mb-3'>{name}</h4>
            <a
              href='#!'
              className='rotate-btn text-dark'
              data-card='card-4'
              onClick={() => this.handleFlipping(id)}
            >
              <MDBIcon icon='redo' /> Click here to learn more
            </a>
          </MDBCardBody>
        </MDBCard>
        <MDBCard className='face back' style={{ height: '300px' }}>
          <MDBCardBody>
            <h4 className='font-weight-bold'>Benefits</h4>
            <hr />
            <p>{body} </p>
            <hr />

            <a
              href='#!'
              className='rotate-btn text-dark'
              data-card='card-4'
              onClick={() => this.handleFlipping(id)}
            >
              <MDBIcon icon='undo' /> Click here to rotate back
            </a>
          </MDBCardBody>
        </MDBCard>
      </MDBRotatingCard>
    </MDBCol>
  </div>
);

and

import React, { Component } from "react";
import "../index.css";
import Carousel from "react-multi-carousel";
import ingredients from "../ingredients.json";
import IngredientsCard from "./IngredientsCard.js";


class IngredientsCarousel extends Component {

  state = {
ingredients
  };



  render() {
    const responsive = {
  desktop: {
    breakpoint: { max: 3000, min: 1024 },
    items: 5,
    slidesToSlide: 1 // optional, default to 1.
  },
  tablet: {
    breakpoint: { max: 1024, min: 464 },
    items: 2,
    slidesToSlide: 2 // optional, default to 1.
  },
  mobile: {
    breakpoint: { max: 464, min: 0 },
    items: 1,
    slidesToSlide: 1 // optional, default to 1.
  }
};
return (
  <div>

      <Carousel
        swipeable={true}
        draggable={true}
        showDots={false}
        responsive={responsive}
        ssr={true}
        // means to render carousel on server-side. infinite={true} keyBoardControl={true} customTransition="all .5" transitionDuration={500} containerClass="carousel-container" removeArrowOnDeviceType={["tablet", "mobile"]} deviceType={this.props.deviceType} dotListClass="custom-dot-list-style" itemClass="carousel-item-padding-40-px"
      >
        {this.state.ingredients.map(merakiIngredients => (
          <IngredientsCard
            id={merakiIngredients.id}
            key={merakiIngredients.id}
            name={merakiIngredients.name}
            image={merakiIngredients.image}
            flipped={merakiIngredients.flipped}
            flipping={merakiIngredients.flipping}
          />
        ))}
      </Carousel>

  </div>
);

} }

export default IngredientsCarousel;


Jakub Chmura staff premium answered 5 years ago

Hi @kpickering

Your IngredientsCarousel looks fine but IngrediensCard still has some issue.

  1. If you use IngreediendsCard as a component to render a card in other components ( as you do in your example) you don't need to set state from 1 to x cards because the state and any other functions from IngreediendsCard that you do not export into other places create a closed environment. The state of each card belongs only to it.
  2. When we delete unnecessary state we don't need any more flipping id's in handleFlipping function because we use only one state.
  3. In MDBRotatingCard component in flipped property, you need to refer directly to the state, which is not a prop.

Check my code and let me know if it will work.

import React, { Component } from 'react';
import {
  MDBCard,
  MDBCol,
  MDBCardBody,
  MDBRotatingCard,
  MDBIcon
} from 'mdbreact';
import Ingredients from '../ingredients';
import ingredients from '../ingredients.json';

class IngredientsCard extends Component {
  state = {
    ingredients,
    flipped: false
  };

  handleFlipping = () => {
    this.setState({ flipped: !this.state.flipped });
  };

  render() {
    const { name, image, body } = this.props;
    return (
      <div>
        <MDBCol style={{ width: '22rem', height: '18rem' }}>
          <MDBRotatingCard
            flipped={this.state.flipped}
            className='text-center h-100 w-100'
          >
            <MDBCard className='face front'>
              <MDBCardBody>
                <img
                  className='card-img-top'
                  src={Ingredients[image]}
                  alt={name}
                />
                <h4 className='font-weight-bold mb-3'>{name}</h4>
                <a
                  href='#!'
                  className='rotate-btn text-dark'
                  data-card='card-4'
                  onClick={() => this.handleFlipping()}
                >
                  <MDBIcon icon='redo' /> Click here to learn more
                </a>
              </MDBCardBody>
            </MDBCard>
            <MDBCard className='face back' style={{ height: '300px' }}>
              <MDBCardBody>
                <h4 className='font-weight-bold'>Benefits</h4>
                <hr />
                <p>{body} </p>
                <hr />

                <a
                  href='#!'
                  className='rotate-btn text-dark'
                  data-card='card-4'
                  onClick={() => this.handleFlipping()}
                >
                  <MDBIcon icon='undo' /> Click here to rotate back
                </a>
              </MDBCardBody>
            </MDBCard>
          </MDBRotatingCard>
        </MDBCol>
      </div>
    );
  }
}

export default IngredientsCard;

Best regards,

Kuba


kpickering answered 5 years ago

Yes, it worked, thank you!


Jakub Chmura staff premium commented 5 years ago

@kpickering, No problem. I'm happy I could help.

Best, Kuba


Please insert min. 20 characters.

FREE CONSULTATION

Hire our experts to build a dedicated project. We'll analyze your business requirements, for free.

Status

Answered

Specification of the issue
  • User: Free
  • Premium support: No
  • Technology: MDB React
  • MDB Version: 4.19.2
  • Device: Samsung Ultra Book ativ
  • Browser: mozilla
  • OS: linux
  • Provided sample code: No
  • Provided link: Yes