React: setState to a single component from multiple mapped instances of the same component

Viewed 155

I have a hover state that changes div background color and adds a <p> tag to a mapped component:

const [isHover, setIsHover] = useState(false)

Here is the mapped component where I set the state:

const AddSectionButton = ({
  isHover,
  setIsHover,
  sections,
  setSections,
  nextSectionId,
  setNextSectionId,
  sectionTitle,
  setSectionTitle,
  sectionId,
}) => {
  return (
    <AddSectionDiv
      onMouseEnter={() => {
        setIsHover(!isHover);
      }}
      onMouseLeave={() => {
        setIsHover(!isHover);
      }}
      style={isHover && { backgroundColor: "#A4AAE0" }}
    >
      {isHover && <p>Add Section</p>}
    </AddSectionDiv>
  );
};

Whenever I hover to a single mapped component, the rest of the mapped components trigger the hover effect as well.

How do I set the state to only the hovered component and not affect the rest?

I thought about using a key, where as you can see in my mapped component, I passed a sectionId prop which contains the key, but I'm confused as to how I should use it.

1 Answers

You can, and should, use a key or any value/property that uniquely identifies the element being hovered.

In the parent use an initially null isHover state.

const [isHover, setIsHover] = useState(null);

And in the children set or clear the isHover state by their id. And check if the current isHover value matches the current sectionId value.

const AddSectionButton = ({
  isHover,
  setIsHover,
  sections,
  setSections,
  nextSectionId,
  setNextSectionId,
  sectionTitle,
  setSectionTitle,
  sectionId,
}) => {
  return (
    <AddSectionDiv
      onMouseEnter={() => {
        setIsHover(sectionId);
      }}
      onMouseLeave={() => {
        setIsHover(null);
      }}
      style={isHover === sectionId && { backgroundColor: "#A4AAE0" }}
    >
      {isHover === sectionId && <p>Add Section</p>}
    </AddSectionDiv>
  );
};

Consider moving/implementing this isHover state internally to each component, the parent component likely doesn't need to concern itself with the hover status of any of its children. Do this and your original logic is fine.

const AddSectionButton = ({
  sections,
  setSections,
  nextSectionId,
  setNextSectionId,
  sectionTitle,
  setSectionTitle,
  sectionId,
}) => {
  const [isHover, setIsHover] = useState(false);

  return (
    <AddSectionDiv
      onMouseEnter={() => {
        setIsHover(true);
      }}
      onMouseLeave={() => {
        setIsHover(false);
      }}
      style={isHover && { backgroundColor: "#A4AAE0" }}
    >
      {isHover && <p>Add Section</p>}
    </AddSectionDiv>
  );
};
Related