javascript - reactjs popover 组件 - 推送属性/事件处理
问题描述
我正在构建一个 reactjs 应用程序——我正在尝试使这个弹出组件模块化——这样我可以让按钮看起来像一个徽章/图标组合——在悬停而不是点击时激活弹出菜单。
这是一个沙箱——但我需要为每个沙箱创建弹出菜单——此时它正在取代按钮。 https://codesandbox.io/s/material-demo-forked-wrn2g?file=/demo.js
这是当前的组件 http://jsfiddle.net/4benm6wo/
import React from 'react';
import Button from '@material-ui/core/Button';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import Badge from '@material-ui/core/Badge';
import PersonIcon from '@material-ui/icons/Person';
import './PopOverMenu.scss';
export default function MenuListComposition() {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
function handleListKeyDown(event) {
if (event.key === 'Tab') {
event.preventDefault();
setOpen(false);
}
}
// return focus to the button when we transitioned from !open -> open
const prevOpen = React.useRef(open);
React.useEffect(() => {
if (prevOpen.current === true && open === false) {
anchorRef.current.focus();
}
prevOpen.current = open;
}, [open]);
return (
<div className="popover-menu">
<div>
<Button
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<Badge badgeContent={11} color="primary">
<PersonIcon />
</Badge>
</Button>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
</div>
);
}
但我想创建一个 popperMenu 组件——我将图标、徽章计数推送到弹出窗口中——所以我需要帮助用道具和状态来实现它。
这是我目前的尝试 http://jsfiddle.net/4benm6wo/1/
class MenuListComposition extends Component {
constructor(props, context) {
super(props, context);
this.state = { open: false };
}
render() {
return (
<div className="popover-menu">
<div>
<Button
ref={anchorRef}
aria-controls={open ? 'menu-list-grow' : undefined}
aria-haspopup="true"
onClick={handleToggle}
>
<Badge badgeContent={11} color="primary">
<PersonIcon />
</Badge>
</Button>
<Popper open={open} anchorEl={anchorRef.current} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList autoFocusItem={open} id="menu-list-grow" onKeyDown={handleListKeyDown}>
<MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleClose}>My account</MenuItem>
<MenuItem onClick={handleClose}>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
</div>
)
}
}
export default MenuListComposition;
所以我会从外壳创建一个类似这样的按钮/徽章弹出窗口
<MenuListComposition badgeCount={10} icon={<PersonIcon />} menu={[{ "label": "Profile", "link": "user/1" }, { "label": "Logout", "link": "logout" }]} />
最新代码
LoginButton.js http://jsfiddle.net/z3L89x2e/
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import Button from '@material-ui/core/Button';
import PopOverMenu from '../_SharedGlobalComponents/PopOverMenu/PopOverMenu';
import MailIcon from '@material-ui/icons/Mail';
import NotificationsIcon from '@material-ui/icons/Notifications';
import PersonIcon from '@material-ui/icons/Person';
class LoggedInButtons extends Component {
/*
constructor(props, context) {
super(props, context);
}
*/
render() {
return (
<div className="login-badges">
<PopOverMenu
icon={<NotificationsIcon />}
badgecount="3"
menu={[
{ "label": "xxx", "value": "/" },
{ "label": "xxxx", "value": "/" },
{ "label": "xxxxx", "value": "/" },
]}
/>
<PopOverMenu
icon={<MailIcon />}
badgecount="5"
menu={[
{ "label": "xxx", "value": "/" },
{ "label": "xxxx", "value": "/" },
{ "label": "xxxxx", "value": "/" },
]}
/>
<PopOverMenu
icon={<PersonIcon />}
badgecount="2"
menu={[
{ "label": "Profile", "value": "/profile" },
{ "label": "My account", "value": "/my-account" },
{ "label": "Logout", "value": "/logout" },
]}
/>
<Button
variant="text"
color="default"
startIcon={<PersonIcon />}
href="/user/view/2"
>
User 2
</Button>
<Button variant="contained" color="secondary" href="/logout">
log out
</Button>
</div>
)
}
}
function mapStateToProps(state) {
return {
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ }, dispatch);
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoggedInButtons))
PopOverMenu.js http://jsfiddle.net/z3L89x2e/2/
import React, { Component } from 'react';
import Button from '@material-ui/core/Button';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import Badge from '@material-ui/core/Badge';
//import './PopOverMenu.scss';
class PopOverMenu extends Component {
constructor(props, context) {
super(props, context);
this.state = { open: false };
this.anchorRef = React.createRef(null);
}
handleToggle = () => {
this.setState({open: !this.state.open});
};
handleClose = () => {
this.setState({open: false});
};
handleListKeyDown = (event) => {
if (event.key === 'Tab') {
event.preventDefault();
this.setState({open: false});
}
}
showMenuItems = () => (
this.props.menu.map((item, i) => (
<MenuItem onClick={this.handleClose}>{item.label}</MenuItem>
))
)
render() {
return (
<div style={{display:'inline', float:'left', marginLeft:'20px', marginTop:'10px'}} className="popover-menu">
<Button
ref={this.anchorRef}
aria-controls={this.state.open ? 'menu-list-grow' : null}
aria-haspopup="true"
onClick={this.handleToggle}
>
<Badge badgeContent={this.props.badgecount} color="primary">
{this.props.icon}
</Badge>
</Button>
<Popper style={{position: 'relative'}} open={this.state.open} role={undefined} transition disablePortal>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={this.handleClose}>
<MenuList autoFocusItem={this.state.open} id="menu-list-grow" onKeyDown={this.handleListKeyDown}>
{this.showMenuItems()}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
);
}
}
export default PopOverMenu;
这是最新的尝试——它几乎成功了——但后来我在 ancor el 中遇到了错误
我正在使用材质 ui 和图标/徽章模块。我开始在将鼠标悬停在菜单上时出错。
我试图遵循这样的方法 - 将 ancor el 放入状态 - 但它不起作用。
如何将弹出框 MATERIAL-UI 功能组件转换为基于类的组件?
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import Button from '@material-ui/core/Button';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import Badge from '@material-ui/core/Badge';
import './PopOverMenu.scss';
class PopOverMenu extends Component {
constructor(props, context) {
super(props, context);
// this.state = { open: false};
// this.anchorRef = React.createRef(null);
this.state = { anchorEl: null, open: false };
}
handleToggle = (event) => {
this.setState({open: !this.state.open});
// this.state.ancherEl ? this.setState({ anchorEl: null }) : this.setState({ anchorEl: event.currentTarget });
};
handleOpen = (event) => {
this.setState({open: true});
console.log("event.currentTarget", event.currentTarget);
this.state.ancherEl ? this.setState({ anchorEl: null }) : this.setState({ anchorEl: event.currentTarget });
};
handleClose = () => {
this.setState({open: false});
//this.setState({ anchorEl: null })
};
handleListKeyDown = (event) => {
if (event.key === 'Tab') {
event.preventDefault();
this.setState({open: false});
}
}
showMenuItems = () => (
this.props.menu.map((item, i) => (
<MenuItem key={i} onClick={this.handleClose}>
<NavLink to={item.value}>{item.label}</NavLink>
</MenuItem>
))
)
render() {
//console.log("this.anchorRef", this.anchorRef)
return (
<div className="popover-menu">
<Button
//ref={this.anchorRef}
aria-controls={this.state.open ? 'menu-list-grow' : null}
aria-haspopup="true"
onClick={this.handleToggle}
onMouseOver={this.handleOpen}
onMouseLeave={this.handleClose}
>
<Badge badgeContent={this.props.badgecount} color="primary">
{this.props.icon}
</Badge>
</Button>
<Popper
className="popper-list"
//anchorEl={this.anchorRef}
open={this.state.open}
anchorEl={this.state.anchorEl}
//role={undefined}
transition
disablePortal
onMouseOver={this.handleOpen}
onMouseLeave={this.handleClose}
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{ transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom' }}
>
<Paper>
<ClickAwayListener onClickAway={this.handleClose}>
<MenuList autoFocusItem={this.state.open} id="menu-list-grow" onKeyDown={this.handleListKeyDown}>
{/*this.showMenuItems()*/}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</div>
);
}
}
export default PopOverMenu;
.popover-menu{
width: 40px;
display: inline;
float: left;
//border: 1px solid red;
//background: blue;
padding: 5px;
.popper-list{
width: 160px;
position: relative!important;
//border: 1px solid blue;
}
}
解决方案
这是一个有效的CodeSandbox
您使用了错误的参考。
您需要在单击时将 ref 分配给 Popper(参见第 89 行)。
为此,您需要使用 setState 而不是 createRef 来设置 ref。悬停激活:您需要在父 div 上使用 onMouseEnter 和 onMouseLeave 切换它
添加 onClick on 按钮以切换弹出器
推荐阅读
- javascript - 我的代码上的 FlatList 无限滚动不起作用
- android - Floating Touch - 如何检测是否有人在 Android 上使用 Qt 悬停屏幕
- javascript - 如何在没有 jQuery 的 iframe 中按标题获取元素?
- angularjs - 如何将输入值移动到小数点后 2 位?
- android - 无法为 org.jetbrains.kotlin.gradle.plugin.KaptExtension 类型的对象获取未知属性“增量”
- arm - 使用 PMU 计算 ARM 上的刻度
- spring-tools-4 - 在 STS 4.0 中忽略未知属性支持缺失
- python - 如何按变量对对象进行分组?
- java - 循环通过特定的小部件
- python - 这是按需设置属性的好习惯(如果在尝试获取属性时不存在)?