React.js sharing state between components updating a dynamic classname on click rendered from .map function

Viewed 97

The code: https://codesandbox.io/s/react-props-conditional-class-o1h6sh

The issue: When you put the child components (Card, Graph) inside the .map function it disrupts the independence of the click, such that clicking either fires all elements, in the case of the 'Card' component or fires both from the 'Graph' component.

The aim: Be able to click any of the 4 links and envoke the dynamic classname "is-red", changing the text colour on the clicked element leaving the others unaffected but persisting the state of the changed element, i.e have the option to toggle any of the four links on and off, whilst not affecting the toggle state of the others.


Passing props to the parent component from two sibling child components works fine, the issue is introduced by the data.map function in addition to nesting the 'Graph' component inside the 'Card' component.

1 Answers

your Child Component "Graph" is inheriting the CSS color style from its Parent Component "Card" whenever the "isCardActive" state equals true.

you also have the same state isCardActive being used for both mapped elements [{ name: "John" }, { name: "Jane" }]. When isCardActive = true all mapped elements will be affected, not only the clicked one.

one solution would be assigning another className to the Child Component, and moving the states inside the Components before mapping it. for example :

styles.css
.is-red {
  color: red;
}
.is-default-color {
  color: black;
}
Graph.js
export default function Graph() {
  const [isGraphActive, setIsGraphActive] = useState(false);
  const handleGraphClick = () => {
    setIsGraphActive((current) => !current);
  };

  return (
    <>
      <div
        className={
          "clicker" + (isGraphActive ? " is-red" : " is-default-color")
        }
        onClick={handleGraphClick}
      >
        Click from Graph
      </div>
    </>
  );
}
Card.js
export default function Card({ graph, name }) {
  const [isCardActive, setIsCardActive] = useState(false);
  const handleCardClick = () => {
    setIsCardActive((current) => !current);
  };
  return (
    <>
      <div className={"card__inner" + (isCardActive ? " is-red" : "")}>
        <div className="clicker" onClick={handleCardClick}>
          Click {name} from card
        </div>
        {graph}
      </div>
    </>
  );
}
App.js
import "./styles.css";
import Card from "./Card";
import Graph from "./Graph";

const data = [{ name: "John" }, { name: "Jane" }];

export default function App() {
  return (
    <div className="App">
      {data.map((el, index) => (
        <Card key={index} name={el.name} graph={<Graph />} />
      ))}
    </div>
  );
}

full sandbox link here

Related