Search code examples
javascriptreactjsionic-frameworkfrontendionic-react

Show <IonCardContent> only when clicking on <IonCardTitle>, considering that we have many <IonCard> elements


From an API request, I get an array of data like below (I get 100 elements, but here I'm showing only 2):

data: Array(2)
0: {cardId: 270, name: "Basic card" , type: "type1"}
1: {cardId: 345, name: "Special card" , type: "type2"}

I show them in my app, using map() method. Each array element is shown in the app through <IonCard>. The card name is in <IonCardTitle> and the card type is in <IonCardContent>.

For the moment, with my actual implementation, when I click on a specific <IonCardTitle>, the contents of all elements are opened.

My goal is: When clicking on a specific element title, open ONLY the content of that element. How can I achieve this?

Any help would be really appreciated.

Cards page:

import {
  IonContent,
  IonPage,
  IonCard,
  IonCardHeader,
  IonCardTitle,
  IonCardContent,
  IonItem,
  IonLabel,
  IonIcon,
} from "@ionic/react";
import React, { useEffect, useRef, useState } from "react";
import { Http} from "@capacitor-community/http";

const Cards: React.FC = () => {

  const [cards, setCards] = useState([]);
const [content, setContent] = useState([]);
const [showContent, setShowContent] = useState([]);
  const mounted = useRef(true);
     
  useEffect(() => {
    mounted.current = true;
    const options = {
      url: "xxx/cards",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
    };
    Http.request({ ...options, method: "GET" }).then(
      (response) => {
        if (mounted.current) {
          setCards(response.data);
  
        }
      },
      (error) => {
        const _content =
          (error.response && error.response.data) ||
          error.message ||
          error.toString();

        setContent(_content);
      }
    );
    return () => {
      mounted.current = false;
    };
  }, []);

  return (
    <React.Fragment>
      <IonPage className="ion-page" id="main-content">
        <IonContent className="ion-padding">
       

          {cards &&
            cards.map((card: any) => (
              <IonCard key={card.cardId}>
                <IonCardHeader>
                  <IonCardTitle>
                    <IonItem
                      button
                      onClick={() => {
                        setShowContent(true);
                        if (showContent === true) {
                          setShowContent(false);
                        }
                      }}
                    >
                      <IonIcon
                        slot="end"
                        icon={showDetails ? arrowDown : arrowForward}
                      ></IonIcon>
                      <IonLabel>
                        <b>{card.name}</b>
                      </IonLabel>
                    </IonItem>
                  </IonCardTitle>
                </IonCardHeader>
                {showContent && (
                  <IonCardContent>
                      <p>Card Type: {card.type}</p>
                    </div>
                  </IonCardContent>
                )}
              </IonCard>
            ))}
        </IonContent>
      </IonPage>
    </React.Fragment>
  );
};

export default Cards;

Solution

    • First, update initial state:

      const [showContent, setShowContent] = useState(null);
      
    • Then, update onClick:

      onClick={() => {
         setShowContent(card.cardId);
         if (showContent === card.cardId) {
           setShowContent(null);
         }
       }}
      
    • Final, update condition render content:

      {showContent === card.cardId && (<IonCardContent>...</IonCardContent>)