2

There is an icon which says call is active, with classname "callConnect". When I hover on the icon, the icon should change, the new icon would be of classname "callDisconnect"

I am just writing a snippet to show my implementation which is not working:

I am using useRef and a functionto update

const hover = useRef(false)
const updateHoverState = (val: boolean) => {
   hover.current = val
}

In my component i have a method which renders the icon as so:

<div onMouseOver={() => {
         updateHoverState(true)
         console.log("hovering", hover.current)}
     } 
     onMouseOut={() => {
         updateHoverState(false)
         console.log("coming out", hover.current)}
     }
>
  {console.log("rendering", hover.current)}  //not working
  {hover.current ? <Icon onClick={() => { }} className={"callDisconnect"} /> : 
              <Icon  onClick={() => { }} className={"callConnect"} />}
</div>

When i run the code, the I see that hover is getting updated to true and false properly when I hover and move out, but the icons are not changing. Re-rendering is not happening. How to fix please help.

Please note: I have already tried using useState, since it did not work, I switched to useRef.

useState:

  const [hover, setHover] = useState(false)
   <div onMouseOver={() => {
             setHover(true)
         } 
         onMouseOut={() => {
             setHover(false)
         }
    >
      {hover ? <Icon onClick={() => { }} className={"callDisconnect"} /> : 
                  <Icon  onClick={() => { }} className={"callConnect"} />} //not updating on hover
    </div>
Chaitra D
  • 177
  • 3
  • 14

2 Answers2

3

The only way to cause a rerender in react is to set state. So instead of using a ref, use a state variable:

const App = () => {
  const [hover, setHover] = React.useState(false);

  return (
    <div
      style={{ width: 50, height: 50 }}
      onMouseOver={() => {
        setHover(true);
      }}
      onMouseOut={() => {
        setHover(false);
      }}
    >
      {hover ? (
        <div style={{ width: 50, height: 50 }} onClick={() => {}} className={"callDisconnect"} />
      ) : (
        <div style={{ width: 50, height: 50 }} onClick={() => {}} className={"callConnect"} />
      )}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("app"));
.callConnect {
  background-color: green
}
.callDisconnect {
  background-color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • Tried this. But, the states do not get updated immediately, so shifted to useRef – Chaitra D Feb 15 '22 at 17:12
  • `the states do not get updated immediately` Like, you have `console.log(hover)` immediately after you set state? Yeah, that's going to show the old value. Setting state does not change your local variables, it just asks react to rerender the component. [See this for more info](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Nicholas Tower Feb 15 '22 at 17:14
  • Yeah, but the icon is not changing on hover – Chaitra D Feb 15 '22 at 17:17
  • I updated my answer to use a snippet, and it appears to be working. You'll need to compare your implementation with mine to see what's different. I don't have access to your Icon component, so i just did a colored div. – Nicholas Tower Feb 15 '22 at 17:29
0

You need to use the useState hook instead of useRef to make your component re-render. Also you can apply the condition directly in the className instead of repeating the whole component just for a class change:

const [hover, setHover] = useState(false);

<div
  onMouseOver={() => setHover(true)}
  onMouseOut={() => setHover(false)}
>
    <Icon onClick={() => {}} className={hover ? "callDisconnect" : "callConnect"} />
</div>;
Avi
  • 1,049
  • 1
  • 5
  • 16