Reputation: 924
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
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
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