Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Mouse Out of Element and Mouse Into Another Element Does Not Reset State

Code: https://codesandbox.io/s/objective-darwin-w0i5pk?file=/src/App.js

Description:
This is just 4 gray squares that each get their own shade of gray. I want to change the background color of each square when the user hovers over each, but I want the hover color to be +10 in RGB of what it was originally.

Issue:
When I mouse/hover out of one of the gray squares and mouse/hover into another gray square, the first square does not switch back to its initial color state.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Help:
Can someone explain why it is doing this and how to fix it because I have no idea?

Note:
I am trying not to use CSS for the hover because I am specifying the backgroundColor with JS.

import React, { useState } from "react";
import "./styles.css";

const tabs = [
  { name: "1", img: [] },
  { name: "2", img: [] },
  { name: "3", img: [] },
  { name: "4", img: [] }
];

const initialState = {};

tabs.forEach((t, i) => {
  initialState[i] = false;
});

export default function App() {
  const [hover, setHover] = useState(initialState);

  return (
    <div className="App">
      {tabs.map((t, i) => {
        const v = 50 - (i + 1) * 10;
        const val = hover[i] ? v + 10 : v;

        return (
          <div
            key={t.name}
            className="tab"
            onMouseOver={() => {
              setHover({
                ...hover,
                [i]: true
              });
            }}
            onMouseLeave={() => {
              setHover({
                ...hover,
                [i]: false
              });
            }}
            onMouseOut={() => {
              setHover({
                ...hover,
                [i]: false
              });
            }}
            style={{
              backgroundColor: `rgb(${val}, ${val}, ${val})`,
              height: "100px",
              width: "100px"
            }}
          >
            <p>{t.name}</p>
          </div>
        );
      })}
    </div>
  );
}
.App {
  font-family: sans-serif;
  text-align: center;
  margin: 0;
  padding: 0;
}
* {
  margin: 0;
  padding: 0;
}

This picture only shows the initial state:

not changing back to initial state

>Solution :

setState calls are not what a human would consider "immediate". Instead, the calls to the state setter as queued inside React internal mechanisms. Consider this:

const [state, setState] = useState(0)

// somewhere

setState(state + 1)
setState(state + 1)

In this case, you do not end up with 2 but 1, because while you call setState twice to increment by one, you really are calling it as:

setState(1)
setState(1)

This is the exact issue in your code with the callbacks, you have

// enter
setState({ ...state, [i]: true })
// leave
setState({ ...state, [i]: false })

so when both get called, you apply the "leave" with the wrong previous state.

This is why setState has another pattern, setState(prevState => nextState)

setState(prevState => prevState + 1)
setState(prevState => prevState + 1)

Like this, you do end up with the value 2 because the second call is then using the "correct" previous state.

In your case, you need:

// enter
setState(prevState => ({ ...prevState, [i]: true }))
// leave
setState(prevState => ({ ...prevState, [i]: false }))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading