Moni
Moni

Reputation: 924

How to show only one active item at a time with react and material ui

I am working on an app, and now I need that when I click a button, add an active class to the card that I am showing.

so far, I am getting the index of the card when I am clicking. after clicking, the desired card will change its background color. However, when I click another button, the previous card is still active and have the same color. I want to show only one active Card at a time.

import React, { Fragment } from "react";
// MUI
import { Container, Grid } from "@material-ui/core";
import { Switch, Route, useRouteMatch } from "react-router-dom";
// Components
import DayCardItem from "./DayCardItem";
import Carousel from "./Carousel";

const DayCard = (props) => {
      <div
        style={{
          maxWidth: 1200,
          marginLeft: "auto",
          marginRight: "auto",
          marginTop: 64,
        }}
      >
        <Carousel show={3}>
          {dailyData &&
            dailyData.map((day, index) => (
              <DayCardItem key={index} day={day} cardIndex={index} />
            ))}
        </Carousel>
      </div>
    </Fragment>
  );
};
export default DayCard;

import React, { useEffect, useState } from "react";
import moment from "moment";
import { Link, useRouteMatch } from "react-router-dom";
// MUI
import { Button, Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
//Redux
import { useSelector } from "react-redux";

const useStyles = makeStyles((theme) => ({
  cardItem: {
    backgroundColor: "#ffffff",
  },
  activeCardItem: {
    color: "#364150",
    backgroundColor: "#efefef"
  },
}));

const DayCardItem = (props) => {
  const { day, cardIndex } = props;

  const classes = useStyles();

  let [activeItem, setActiveItem] = useState(null);


  const handleActiveCard = (cardId) => {
    let actualCardId = activeItem;
    actualCardId = cardId;
    setActiveItem(actualCardId);
    };

  return (
    <div className={`${cardIndex === activeItem && classes.activeCardItem}`}>
      <Link to={`${url}/${paramUrlId}`}>
        <Button
          variant="contained"
          color="primary"
          onClick={() => handleActiveCard(cardIndex)}
        >
          Weather Details
        </Button>
      </Link>
    </div>
  );
};

export default DayCardItem;

Upvotes: 1

Views: 2562

Answers (2)

Drew Reese
Drew Reese

Reputation: 202605

Each DayCardItem component has its own active state, so none of them know about any other active items to inactivate themselves. A solution here is to lift the activeItem state to the parent and track only a single active item at a time.

const DayCard = (props) => {
  const [activeIndex, setActiveIndex] = useState(null);

  // set new index if different, otherwise set back to null to deactivate
  const handleActiveCard = (index) => {
    setActiveIndex(activeIndex => activeIndex === index ? null : index);
  }; 

  ...

      <div
        style={{
          maxWidth: 1200,
          marginLeft: "auto",
          marginRight: "auto",
          marginTop: 64,
        }}
      >
        <Carousel show={3}>
          {dailyData &&
            dailyData.map((day, index) => (
              <DayCardItem
                key={index}
                day={day}
                setActive={setActiveIndex} // <-- pass callback
                isActive={activeIndex === index} // <-- compute if active
              />
            ))}
        </Carousel>
      </div>
    </Fragment>
  );
};
const DayCardItem = ({ day, isActive, setActive }) => {
  const classes = useStyles();

  return (
    <div className={`${isActive && classes.activeCardItem}`}>
      <Link to={`${url}/${paramUrlId}`}>
        <Button
          variant="contained"
          color="primary"
          onClick={() => setActive(cardIndex)}
        >
          Weather Details
        </Button>
      </Link>
    </div>
  );
};

Upvotes: 1

Kartik Malik
Kartik Malik

Reputation: 495

you need to move the state to its parent

import React, { Fragment } from "react";
// MUI
import { Container, Grid } from "@material-ui/core";
import { Switch, Route, useRouteMatch } from "react-router-dom";
// Components
import DayCardItem from "./DayCardItem";
import Carousel from "./Carousel";

const DayCard = (props) => {
let [activeItem, setActiveItem] = useState(null);
return(
      <div
        style={{
          maxWidth: 1200,
          marginLeft: "auto",
          marginRight: "auto",
          marginTop: 64,
        }}
      >
        <Carousel show={3}>
          {dailyData &&
            dailyData.map((day, index) => (
              <DayCardItem 
                  key={index} 
                  day={day} 
                  cardIndex={index} 
                  activeItem={activeItem} 
                  setActiveItem={setActiveItem}
               />
            ))}
        </Carousel>
      </div>
    </Fragment>
  );
};
export default DayCard;

DayCardItem.js

import React, { useEffect, useState } from "react";
import moment from "moment";
import { Link, useRouteMatch } from "react-router-dom";
// MUI
import { Button, Typography } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
//Redux
import { useSelector } from "react-redux";

const useStyles = makeStyles((theme) => ({
  cardItem: {
    backgroundColor: "#ffffff",
  },
  activeCardItem: {
    color: "#364150",
    backgroundColor: "#efefef"
  },
}));

const DayCardItem = (props) => {

  const { day, cardIndex } = props;
  const { activeItem, setActiveItem } = props;
  const classes = useStyles();


  const handleActiveCard = (cardId) => {
    let actualCardId = cardId;
    setActiveItem(actualCardId);
    };

  return (
    <div className={`${cardIndex === activeItem && classes.activeCardItem}`}>
      <Link to={`${url}/${paramUrlId}`}>
        <Button
          variant="contained"
          color="primary"
          onClick={() => handleActiveCard(cardIndex)}
        >
          Weather Details
        </Button>
      </Link>
    </div>
  );
};

export default DayCardItem;

This should resolve your issues, basically as you are creating the active state inside the DayCardItem so it is specific to every item, if you shift it to DayCard then it will be common for all the items which is your requirement for this use case.

Side tip: Don't use the index as a key

Upvotes: 2

Related Questions