鼠标离开元素和鼠标进入另一个元素不会重置状态

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

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

描述: 这只是 4 个灰色方块,每个方块都有自己的灰色阴影。当用户将鼠标悬停在每个正方形上时,我想更改每个正方形的背景颜色,但我希望悬停颜色在原来的 RGB 中为 +10。

问题: 当我 mouse/hover 离开一个灰色方块并 mouse/hover 进入另一个灰色方块时,第一个方块不会切换回其初始颜色状态。

求助: 有人可以解释为什么这样做以及如何解决它,因为我不知道吗?

注: 我尽量不使用 CSS 进行悬停,因为我用 JS 指定了 backgroundColor。

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;
}

本图仅展示初始状态:

发生这种情况是因为您还在您的状态中保留了以前的值。你应该这样更新

onMouseOver={() => {
              setHover({
                [i]: true
              });
            }}
            onMouseLeave={() => {
              setHover({
                [i]: false
              });
            }}
            onMouseOut={() => {
              setHover({
                [i]: false
              });
            }}

setState 调用不是人类认为的“立即”。相反,对状态 setter 的调用在 React 内部机制中排队。考虑一下:

const [state, setState] = useState(0)

// somewhere

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

在这种情况下,您最终得到的不是 2,而是 1,因为当您两次调用 setState 以递增 1 时,您实际上是将其称为:

setState(1)
setState(1)

这正是您的回调代码中的问题,您有

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

所以当两者都被调用时,您应用错误的先前状态的“离开”。

这就是为什么 setState 有另一种模式,setState(prevState => nextState)

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

像这样,您最终会得到值 2,因为第二次调用使用的是“正确”的先前状态。

对于您的情况,您需要:

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