【问题标题】:Why does setState(null) not change the state value为什么 setState(null) 不改变状态值
【发布时间】:2021-05-10 13:43:12
【问题描述】:

我正在尝试使用 React 的 setState 挂钩打开和关闭我的 Material UI <Menu />。我以this sandbox code 为例。然而,我的实现似乎并没有关闭菜单。

打开它可以正常工作(使用setAnchorEl(event.currentTarget)),但使用setAnchorEl(null) 关闭它并不能以某种方式。函数 handleClose() 运行(console.log() 打印正常),但 anchorEl 的值没有改变。我使用 useEffect() 钩子检查了这一点,它在初始化时打印 null,在打开时打印 <li> 元素,而在尝试关闭时什么也不打印。

我已经尝试在 (set)anchorElisProfileMenuOpen 定义中使用 let 而不是 const,但这不起作用。我还检查了this post,但我的<MenuAppBar /> 组件没有被卸载(它无条件地呈现在根目录)。

我一定忽略了一些细节。有人能在下面看到吗?

const {useState, useEffect} = React;
const { ClickAwayListener, Menu, MenuItem, IconButton } = MaterialUI;

function MenuAppBar(props) {
  const [anchorEl, setAnchorEl] = useState(null);
  const isProfileMenuOpen = Boolean(anchorEl);

  // For debugging purposes:
  useEffect(() => {
    console.log(anchorEl);
  }, [anchorEl]);

  const handleClose = () => {
    console.log("Closing menu"); // This prints, so function runs
    setAnchorEl(null); // This doesn't seem to set anchorEl to null
  };

  const handleProfileMenuOpen = (event) => {
    setAnchorEl(event.currentTarget); // This does work
  }

  return (
    <ClickAwayListener onClickAway={handleClose}>
      <MenuItem onClick={handleProfileMenuOpen}>
        <IconButton
          style={{ backgroundColor: "#F5B089" }}
        />
        <Menu
          anchorEl={anchorEl}
          keepMounted
          open={isProfileMenuOpen}
          onClose={handleClose}
        >
          <MenuItem onClick={handleClose}>Profile</MenuItem>
          <MenuItem onClick={handleClose}>My account</MenuItem>
          <MenuItem>Logout</MenuItem>
        </Menu>
      </MenuItem>
    </ClickAwayListener>
  );
}

ReactDOM.render(
  <MenuAppBar />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script>
<div id="react"></div>

【问题讨论】:

  • 这是给你一个最小的可重现示例的缺点;)。知道什么会在反应中产生噪音设置状态吗?
  • @T.J.Crowder 我已经为你提供了一个 sn-p。确实有些奇怪的行为。
  • @samthecodingman - 谢谢,希望 OP 也能欣赏它。只是setAnchorEl 被第二次调用,在调用设置null 之后,将相同的li 放回其中。

标签: javascript reactjs react-hooks use-state


【解决方案1】:

发生的事情是在调用handleClose 之后,再次调用handleProfileMenuOpen。所以你最终会在同一个点击事件期间对setAnchorEl 进行两次调用,所以 React 只重新渲染组件一次——将它设置为 handleProfileMenuOpen 设置的值(或者如果它是相同的 @987654325 则根本不渲染@,就像它在堆栈片段中一样)。

如果您阻止事件传播,它会阻止 handleProfileMenuOpen 调用:

const handleClose = (event) => {
    console.log("Closing menu");
    setAnchorEl(null);
    event.stopPropagation(); // <=====
};

现场示例:

const {useState, useEffect} = React;
const { ClickAwayListener, Menu, MenuItem, IconButton } = MaterialUI;

function MenuAppBar(props) {
  const [anchorEl, setAnchorEl] = useState(null);
  const isProfileMenuOpen = Boolean(anchorEl);
  
  console.log(anchorEl);

  const handleClose = (event) => {
    console.log("Closing menu"); // This prints, so function runs
    setAnchorEl(null); // This doesn't seem to set anchorEl to null
    event.stopPropagation();
  };

  const handleProfileMenuOpen = (event) => {
    console.log("Opening menu");
    setAnchorEl(event.currentTarget); // This does work
  }

  return (
    <ClickAwayListener onClickAway={handleClose}>
      <MenuItem onClick={handleProfileMenuOpen}>
        <IconButton
          style={{ backgroundColor: "#F5B089" }}
        />
        <Menu
          anchorEl={anchorEl}
          keepMounted
          open={isProfileMenuOpen}
          onClose={handleClose}
        >
          <MenuItem onClick={handleClose}>Profile</MenuItem>
          <MenuItem onClick={handleClose}>My account</MenuItem>
          <MenuItem>Logout</MenuItem>
        </Menu>
      </MenuItem>
    </ClickAwayListener>
  );
}

ReactDOM.render(
  <MenuAppBar />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@material-ui/core@latest/umd/material-ui.development.js"></script>
<div id="react"></div>

旁注:如果您想看到 anchorEl 在组件重新渲染时发生变化,则无需调用 useEffect,只需将其记录在组件函数的顶层(或在 @ 上放置断点) 987654332@ 线)。请记住,每次渲染组件时都会调用组件函数,因此始终会向您显示anchorEl 的最新版本。

【讨论】:

    猜你喜欢
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 2015-08-27
    相关资源
    最近更新 更多