Reactjs all cards flipping instead of 1

I am creating a Reactjs card memory game. The idea is that you need to click on a card and match it with an identical one. I have the flip animations working, but my issue is with state. When I click on 1 card, all of them flip instead of 1. Hopefully someone can shed some light on this. Thank you,

App.js

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFlipped: false,
      visible: false,
    }
  }

  componentDidMount() {
    this.setState({ visible: true });
  }

  toggleClass = () => {
        this.setState(
            (prevState) => {
              console.log(prevState); // refers to previous state
              return {
                isFlipped: !prevState.isFlipped,
              };
            },
            () => console.log(this.state)
          ) // refers to updated state
  }

  render() {
    return (
      <>
        <h3>Welcome!!</h3>
        <div className="cards-container">
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          <Card 
            isFlipped={this.state.isFlipped} 
            visible={this.state.visible} 
            toggleClass={ this.toggleClass }
          />
          
        </div>
      </>
    );
  }
}

Card.js

class Card extends Component {

  render() {
      const { isFlipped, visible, toggleClass } = this.props

      render() {
      const { isFlipped, visible, toggleClass } = this.props

        return (
          <div className={visible ? "fade-in scene" : "scene"}>
            <div
              className={isFlipped ? "card is-flipped" : "card"}
              onClick={toggleClass}
            >
              <div className="card__face card__face--front"></div>
              <div className="card__face card__face--back">
                <FontAwesomeIcon 
                  icon={ faOtter } 
                  color="coral" 
                  size="2x"
                />
              </div>
            </div>
          </div>
        )     
  } 
}

App.css

body { 
  font-family: sans-serif; 
  background-image: linear-gradient(rgba(0, 0, 0, 0.883),  rgba(0, 0, 0, 0.103)), url(../assets/imgs/wood.jpg);
  width: 100%;
  height: 100vh;
  background-size: cover;
  background-position: center;
  top:0px; bottom: 0px;
  /* height: 100%; */
  background-repeat: repeat;
}

.scene {
  width: 100px;
  height: 130px;
  perspective: 600px;
  position: relative;
  margin: 5px;
  box-shadow: 1em 1em 2em rgba(0, 0, 0, 0.945);
  border-radius: 10px;
}

/* Animate flip when cards are clicked */

.card {
  cursor: pointer;
  position: relative;
  width: 100%;
  height: 100%;
  transition: transform 1.3s;
  transform-style: preserve-3d;
}

.card.is-flipped {
  transform: rotateY(180deg);
}

.card__face {
  position: absolute;
  width: 100%;
  height: 100%;
  line-height: 145px;
  text-align: center;
  border-radius: 10px;
}

.card__face--front {
  background-image: linear-gradient(to bottom, #070809, #0a0a0f, #120a11, #180b0e, #1c0d06);
}

.card__face--back {
  background-image: linear-gradient(to bottom, #070809, #0a0a0f, #120a11, #180b0e, #1c0d06);
  transform: rotateY(180deg);
}

/* centering cards container and flexing cards inside */

.cards-container {
  width: 600px;
  height: 200px;
  position: absolute;
  left: 30%;
  top: 30%;
  margin: -100px 0 0 -150px;
  display: flex;
  flex-wrap: wrap;
}

/* Animations */

@keyframes wiggle {
  0% { transform: rotate(0deg); }
 80% { transform: rotate(0deg); }
 85% { transform: rotate(5deg); }
 95% { transform: rotate(-5deg); }
100% { transform: rotate(0deg); }
}

.fade-in {
  display: inline-block;
  animation: wiggle 3s infinite;
}

.fade-in:hover {
  animation: none;
}

/* Font Awesome Icons */
i {
  color: #fff;
}

It was impossible to fit a good explanation here. But yet the main thing is that because the state is on a parent class, when you click a card, it sets the state on a parent class.

When you change the state of a parent class, it re-renders all the children, so the cards were modified.

One solution is to pass the state to each children. Also I’ve shorten the code in a couple of lines, and this can probably be improved.

Take a look.

1 Like

misterybodon thank you so much!! You’ve saved my butt as I’ve been stuck on this for 2 days now. The funny thing is that I originally had this working and did have the state on the Card component, but wanted it on the parent class because I needed to add state for something else. Clearly there are knowledge gaps in my react knowledge and I think I really gotta go back to the basics.

Just curious where did you go to learn react? What best helped you out to figure it all out? Thanks again for the help!

Oh yea also not sure if you already looked into <></> , but these are a simpler way of writing <React.Fragment></React.Fragment>.

@iraqwarvet31 I’ve read from react docs. Take a look.

Thanks, I had no idea, and to be honest I just assumed it just same than a <div>, so thanks for the info.

Any question you have, I would be pleased to look into, and solve it together.

Good luck!

1 Like