首页 > 解决方案 > 反应钩子 setState 没有正确地重新渲染组件

问题描述

我有一个组件(切换菜单列表),它应该在单击按钮时加载一个子组件(切换菜单项)。

下面是它的工作原理。

'btnId' 初始状态 = null

-> 按钮单击

-> 将状态更新为索引号 1

-> (btnId !== null) && 加载子组件

但是,子组件不会在状态更新时显示。

如果我将初始化状态设置为 1,它会在按钮单击时显示。

toggleMenuList.js

import React, { useState, useRef } from 'react';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
/* --- Components --- */
import Loader from '../../shared/loader';

const ToggleMenuItems = Loader({
  loader: () =>
    import('./toggleMenuItems' /* webpackChunkName: 'ToggleMenuItems' */),
});

const styles = theme => ({
...
});

const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
  const [open, setOpen] = useState(false);
  const [btnId, setBtnId] = useState(null);
  const anchorRef = useRef(null);

  const handleToggle = async id => {
    await setBtnId(id);
    return setOpen(prevOpen => !prevOpen);
  };

  const handleClose = event => {
   ...
  };

  console.log('Toggle Menu List is rendered');
  console.log('btnId: ', btnId);

  return (
    <React.Fragment>
      <div className={`nav-menu ${classes.root}`}>
        {navAdminList.map(e => (
          <Button
            key={e.id}
            ref={anchorRef}
            aria-owns={open ? 'menu-list-grow' : undefined}
            aria-haspopup="true"
            onClick={() => handleToggle(e.id)}
            className={e.className}
          >
            {e.name}
          </Button>
        ))}
      </div>
      {btnId !== null && (
        <ToggleMenuItems
          handleClose={handleClose}
          open={open}
          anchorRef={anchorRef}
          items={navAdminItems[btnId]}
        />
      )}
    </React.Fragment>
  );
};

export default withStyles(styles)(ToggleMenuList);

toggleMenuItems.js

const ToggleMenuItems = ({ handleClose, open, anchorRef, items }) => {

  console.log('Toggle Menu Items is rendered.');
  console.log('open: ', open);

  return (
    <Popper
      open={open}
      anchorEl={anchorRef.current}
      keepMounted
      transition
      disablePortal
    >
      {({ TransitionProps, placement }) => (
        <Grow
          {...TransitionProps}
          style={{
            transformOrigin:
              placement === 'bottom' ? 'center top' : 'center bottom',
          }}
        >
          <Paper id="menu-list-grow">
            <ClickAwayListener onClickAway={handleClose}>
              <MenuList>
                {items.map(e => (
                  <MenuItem key={e.id} onClick={handleClose}>
                    <Link to={e.to} className={e.className}>
                      {e.name}
                    </Link>
                  </MenuItem>
                ))}
              </MenuList>
            </ClickAwayListener>
          </Paper>
        </Grow>
      )}
    </Popper>
  );
};

export default ToggleMenuItems;

这是我得到的控制台日志。

* On page load
toggleMenuList.js:  Toggle Menu List is rendered
toggleMenuList.js:  btnId:  null

* On button click
toggleMenuList.js:  Toggle Menu List is rendered
toggleMenuList.js:  btnId:  1
toggleMenuList.js:  Toggle Menu List is rendered
toggleMenuList.js:  btnId:  1

toggleMenuItems.js: Toggle Menu Items is rendered.
toggleMenuItems.js: open: true

结果

标签: material-uireact-hooks

解决方案


我用不同的方法解决了这个问题。我将 activeId 状态设置为初始值“null”,在单击按钮时会更新为“clicked button id”。并且仅当“button Id”匹配“activeId”时才加载子组件。

而且我还在子组件中添加了这个条件。

const isOpen = activeId === id;

  return (
    <Popper
      open={isOpen}

这样,当一个按钮的 open 为 true 时,其余的设置为 false。

toggleMenuList.js

const ToggleMenuList = ({ navAdminList, navAdminItems, classes }) => {
  const [activeId, setActiveId] = useState(null);
  const anchorRef = useRef(null);
  const handleToggle = id => {
    setActiveId(id);
  };
  const handleClose = (event) => {
    if (anchorRef.current && anchorRef.current.include(event.target)) {
      return;
    }
    setActiveId(null);
  };

  return (
    <React.Fragment>
      <div className={`nav-menu ${classes.root}`}>
        {navAdminList.map(e => (
          <div key={e.id}>
            <Button
              ref={anchorRef}
              aria-owns={activeId === e.id ? 'menu-list-grow' : undefined}
              aria-haspopup="true"
              onClick={() => handleToggle(e.id)}
              className={e.className}
            >
              {e.name}
            </Button>
            {activeId === e.id && (
              <ToggleMenuItems
                id={e.id}
                activeId={activeId}
                handleClose={handleClose}
                anchorRef={anchorRef}
                items={navAdminItems[e.id]}
              />
            )}
          </div>
        ))}
      </div>
    </React.Fragment>
  );
};

toggleMenuItems.js

const ToggleMenuItems = ({ id, activeId, handleClose, anchorRef, items }) => {
  const isOpen = activeId === id;

  return (
    <Popper
      open={isOpen}
      anchorEl={anchorRef.current}
      keepMounted
      transition
      disablePortal
    >
      {({ TransitionProps, placement }) => (
        <Grow
          {...TransitionProps}
          style={{
            transformOrigin:
              placement === 'bottom' ? 'center top' : 'center bottom',
          }}
        >
          <Paper id="menu-list-grow">
            <ClickAwayListener onClickAway={() => handleClose(id)}>
              <MenuList>
                {items.map(e => (
                  <MenuItem key={e.id} onClick={() => handleClose(id)}>
                    <Link to={e.to} className={e.className}>
                      {e.name}
                    </Link>
                  </MenuItem>
                ))}
              </MenuList>
            </ClickAwayListener>
          </Paper>
        </Grow>
      )}
    </Popper>
  );
};


推荐阅读