#javascript #reactjs #react-hooks
#javascript #reactjs #реагирование-перехваты
Вопрос:
Я написал простой перехват для отображения всплывающего меню при нажатии кнопки, у меня возникла проблема с передачей buttonRef , похоже, он передает его компоненту, однако элемент позиционируется неправильно, как будто он вообще не передается.
Я включил пример в codeSanbox: https://codesandbox.io/s/9o03qx3n3o
Я подтвердил, что это работает, когда не внутри перехвата, инструменты React dev показывают, что он передает один и тот же элемент в обоих случаях. Я пытался использовать forwardRef, та же проблема, я пытался использовать стандартное состояние для хранения ссылки, но безрезультатно;
--- Hook ---
const usePopperMenu = btnRef => {
const [open, setOpen] = useState(false);
const handleClose = event => {
if (btnRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
const elems = ({ children }) => (
<Popper
open={open}
anchorEl={btnRef.current}
placement="bottom"
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
id="menu-list-grow"
style={{
transformOrigin:
placement === "bottom" ? "center top" : "center bottom"
}}
>
<Paper>
<ClickAwayListener onClickAway={e => handleClose(e)}>
<MenuList>{children}</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
return [elems, setOpen, open];
};
--- App/Component ---
function App() {
const btnRef = useRef(null);
const [Menu, setOpen, open] = usePopperMenu(btnRef);
return (
<div className="App">
<div>
<Button
buttonRef={btnRef}
onClick={() => setOpen(!open)}
variant="outlined"
color="secondary"
>
Click Me!
</Button>
<Menu>
<MenuItem>
<Typography color="textPrimary">Menu Item 1</Typography>
</MenuItem>
<MenuItem>
<Typography color="textPrimary">Menu Item 2</Typography>
</MenuItem>
</Menu>
</div>
</div>
);
}
Ответ №1:
Для меня это хорошо работает — проблема с вашими стилями css. Контейнер кнопок имеет полный размер, и display:grid
поэтому выпадающий список неуместен
Попробуйте:
.App {
display: flex;
justify-content: center;
}
Ответ №2:
Проблема в вашем коде заключается в том, что вы запускаете перехват и передаете ему ссылку, которая изначально не назначена элементу button. Он будет назначен только после первоначального рендеринга и сообщения о том, что вы не перерисовываете свое приложение для повторной инициализации вашего перехвата, чтобы оно получило правильную ссылку. Также вы пытаетесь вписаться в пользовательский перехват, где они вам не нужны. То, чего вы пытаетесь достичь, можно просто сделать с помощью компонентной модели.
Ваш код Popper будет выглядеть так
import React, { useState } from "react";
import Grow from "@material-ui/core/Grow";
import Paper from "@material-ui/core/Paper";
import Popper from "@material-ui/core/Popper";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import MenuList from "@material-ui/core/MenuList";
const PopperMenu = ({ target, children, open, handleClose }) => (
<Popper
open={open}
anchorEl={target.current}
placement="bottom"
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
id="menu-list-grow"
style={{
transformOrigin:
placement === "bottom" ? "center top" : "center bottom"
}}
>
<Paper>
<ClickAwayListener onClickAway={e => handleClose(e)}>
<MenuList>{children}</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
);
export default PopperMenu;
и вы можете использовать это как
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import Button from "@material-ui/core/Button";
import Typography from "@material-ui/core/Typography";
import MenuItem from "@material-ui/core/MenuItem";
import PopperMenu from "./hooks";
import "./styles.css";
function App() {
const btnRef = useRef(null);
const [open, setOpen] = useState(false);
const handleClose = event => {
if (btnRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
return (
<div className="App">
<div>
<Button
buttonRef={btnRef}
onClick={() => setOpen(!open)}
variant="outlined"
color="secondary"
>
Click Me!
</Button>
<PopperMenu target={btnRef} handleClose={handleClose} open={open}>
<MenuItem>
<Typography color="textPrimary">Menu Item 1</Typography>
</MenuItem>
<MenuItem>
<Typography color="textPrimary">Menu Item 2</Typography>
</MenuItem>
</PopperMenu>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);