Реагирующие хуки setState неправильно перерисовывают компонент

У меня есть компонент (переключатель списка меню), который должен загружать дочерний компонент (переключать элементы меню) при нажатии кнопки.

Вот как это работает.

Начальное состояние 'btnId' = ноль

-> нажатие кнопки

-> обновить состояние до индекса номер 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.info('Toggle Menu List is rendered');
  console.info('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.info('Toggle Menu Items is rendered.');
  console.info('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

результат

  • Значение состояния обновляется.

  • Дочерний компонент, кажется, загружен. (console.info запускается)

  • Но он не отображается.

Можете ли вы сделать минимальный функциональный пример на CodeSandbox?

cbdeveloper 04.06.2019 10:27

Я решил проблему. Спасибо за комментарий :)

Jiah827 05.06.2019 16:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
526
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я решил проблему другим способом. Я установил состояние activeId с начальным значением «null», которое обновляется с помощью «идентификатора нажатой кнопки» при нажатии кнопки. И загружайте дочерний компонент только тогда, когда «идентификатор кнопки» соответствует «activeId».

И я также добавляю это условие в дочерний компонент.

const isOpen = activeId === id;

  return (
    <Popper
      open = {isOpen}

Таким образом, когда открытие одной кнопки имеет значение 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>
  );
};

Другие вопросы по теме