I'm implementing a menu that opens when the user clicks on an Avatar. The problem is that the menu is rendering in a completely different place:
The avatar is the green "OB" button on the right. There is no console error and inspecting the Popover element, it's receiving the anchorEl prop:
The language menu, on the right of the avatar, renders just fine, opening where it should open. My code seems fine, I'm really not sure why the position is wrong:
export function DashboardNavbar({ setDrawer }) {
// translation hook
const { i18n } = useTranslation("navbar");
// config drawer state
const [configDrawer, setConfigDrawer] = useState(false);
// config menu state
const configMenuState = usePopupState({
variant: "popover",
popupId: "configMenu"
});
// avatar id
const [cookie] = useCookies("userInfo");
const decodedToken = decodeToken(cookie.userInfo.token);
const avatarId =
decodedToken.firstName.charAt(0) + decodedToken.lastName.charAt(0);
function DesktopNavbar() {
return (
<>
<StyledDashboardNavbar>
<Container maxWidth="lg">
<div
style={{
display: "flex",
justifyContent: "flex-end"
}}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
{...bindTrigger(configMenuState)}
>
{avatarId}
</Avatar>
<DashboardMenu
bindMenu={bindMenu}
menuState={configMenuState}
/>
<LanguageMenu i18n={i18n} />
</div>
</Container>
</StyledDashboardNavbar>
</>
);
}
function MobileNavbar() {
return (
<>
<StyledDashboardNavbar>
<Container maxWidth="md">
<div className="navbar">
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<MenuIcon
color="secondary"
onClick={() => setDrawer(true)}
/>
</div>
<div
className="logo"
onClick={() => setConfigDrawer(true)}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
>
{avatarId}
</Avatar>
</div>
</div>
</Container>
</StyledDashboardNavbar>
<AvatarDrawer
drawer={configDrawer}
setDrawer={setConfigDrawer}
/>
</>
);
}
return window.innerWidth > 480 ? <DesktopNavbar /> : <MobileNavbar />;
}
I'm using the material-ui-popup-state, but I tried implementing "on-hand" without this package and the result was the same.
Any help on this is appreciated. Thanks in advance
The problem is the nesting of DesktopNavbar within DashboardNavbar. This means that every time DashboardNavbar re-renders, DesktopNavbar will be redefined. Since DesktopNavbar will be a new function compared to the previous render of DashboardNavbar, React will not recognize it as the same component type and DesktopNavbar will be re-mounted rather than just re-rendered. Since the menu state is maintained within DashboardNavbar, opening the menu causes a re-render of DashboardNavbar and therefore a re-definition of DesktopNavbar so, due to the re-mounting of DesktopNavbar and everything inside it, the anchor element passed to the menu will no longer be part of the DOM.
It is almost always a bad idea to nest the definitions of components, because the nested components will be treated as a new element type with each re-render of the containing component.
From https://reactjs.org/docs/reconciliation.html#elements-of-different-types:
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Going from <a> to <img>, or from <Article> to <Comment>, or from <Button> to <div> - any of those will lead to a full rebuild.
When you redefine DesktopNavbar and MobileNavbar on re-render of DashboardNavbar, the entire tree of DOM elements within those will be removed from the DOM and re-created from scratch rather than just applying changes to the existing DOM elements. This has a big performance impact and also causes behavior issues like the one you experienced where elements that you are referring to are unexpectedly no longer part of the DOM.
If you instead move DesktopNavbar and MobileNavbar to the top-level and pass any dependencies from DashboardNavbar as props, this will cause DesktopNavbar to be recognized by React as a consistent component type across re-renders of DashboardNavbar. LanguageMenu doesn't have the same issue, because presumably its state is managed internally, so opening it doesn't cause a re-render of DashboardNavbar.
Sample restructuring of code (not executed, so may have minor errors):
function DesktopNavbar({configMenuState, i18n}) {
return (
<>
<StyledDashboardNavbar>
<Container maxWidth="lg">
<div
style={{
display: "flex",
justifyContent: "flex-end"
}}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
{...bindTrigger(configMenuState)}
>
{avatarId}
</Avatar>
<DashboardMenu
bindMenu={bindMenu}
menuState={configMenuState}
/>
<LanguageMenu i18n={i18n} />
</div>
</Container>
</StyledDashboardNavbar>
</>
);
}
function MobileNavbar({setDrawer, configDrawer, setConfigDrawer, avatarId}) {
return (
<>
<StyledDashboardNavbar>
<Container maxWidth="md">
<div className="navbar">
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<MenuIcon
color="secondary"
onClick={() => setDrawer(true)}
/>
</div>
<div
className="logo"
onClick={() => setConfigDrawer(true)}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
>
{avatarId}
</Avatar>
</div>
</div>
</Container>
</StyledDashboardNavbar>
<AvatarDrawer
drawer={configDrawer}
setDrawer={setConfigDrawer}
/>
</>
);
}
export function DashboardNavbar({ setDrawer }) {
// translation hook
const { i18n } = useTranslation("navbar");
// config drawer state
const [configDrawer, setConfigDrawer] = useState(false);
// config menu state
const configMenuState = usePopupState({
variant: "popover",
popupId: "configMenu"
});
// avatar id
const [cookie] = useCookies("userInfo");
const decodedToken = decodeToken(cookie.userInfo.token);
const avatarId =
decodedToken.firstName.charAt(0) + decodedToken.lastName.charAt(0);
return window.innerWidth > 480 ? <DesktopNavbar configMenuState={configMenuState} i18n={i18n} /> : <MobileNavbar setDrawer={setDrawer} configDrawer={configDrawer} setConfigDrawer={setConfigDrawer} avatarId={avatarId} />;
}
An alternative way to fix this is to just eliminate the nested components, so that DashboardNavbar is a single component:
export function DashboardNavbar({ setDrawer }) {
// translation hook
const { i18n } = useTranslation("navbar");
// config drawer state
const [configDrawer, setConfigDrawer] = useState(false);
// config menu state
const configMenuState = usePopupState({
variant: "popover",
popupId: "configMenu"
});
// avatar id
const [cookie] = useCookies("userInfo");
const decodedToken = decodeToken(cookie.userInfo.token);
const avatarId =
decodedToken.firstName.charAt(0) + decodedToken.lastName.charAt(0);
const useDesktopLayout = window.innerWidth > 480;
return <>
{useDesktopLayout &&
<StyledDashboardNavbar>
<Container maxWidth="lg">
<div
style={{
display: "flex",
justifyContent: "flex-end"
}}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
{...bindTrigger(configMenuState)}
>
{avatarId}
</Avatar>
<DashboardMenu
bindMenu={bindMenu}
menuState={configMenuState}
/>
<LanguageMenu i18n={i18n} />
</div>
</Container>
</StyledDashboardNavbar>
}
{!useDesktopLayout &&
<>
<StyledDashboardNavbar>
<Container maxWidth="md">
<div className="navbar">
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<MenuIcon
color="secondary"
onClick={() => setDrawer(true)}
/>
</div>
<div
className="logo"
onClick={() => setConfigDrawer(true)}
>
<Avatar
style={{
backgroundColor:
theme.palette.secondary.main
}}
>
{avatarId}
</Avatar>
</div>
</div>
</Container>
</StyledDashboardNavbar>
<AvatarDrawer
drawer={configDrawer}
setDrawer={setConfigDrawer}
/>
</>
}
</>;
}
Related answers:
React Material-UI menu anchor broken by react-window list
React/MUI Popover positioning incorrectly with anchorPosition
Material UI Popover is thrown to the top left when used on an inline button in a table with a unique key
Related
I have a code with which the user can select a file from their device. The Card component will display its name and operations that can be done with the file.
But my problem is that I don't know how to close this component if the user wants to cancel the action.
export default function DisplaySelectedFile() {
const [fileName, setFileName] = useState("");
console.log(setFileName)
return (
<div>
<SelectFileButton setFileName={setFileName} />
{fileName && <Card sx={styles.CommonStyle}>
<Stack spacing={10} direction="row" style={{paddingTop: "20px", paddingLeft: "10px"}}>
<div>{fileName}</div>
<Stack spacing={3} direction="row">
<div>Convert to</div>
<ConvertToFormatFile></ConvertToFormatFile>
</Stack>
<Button>CONVERT</Button>
<CloseIcon/>
</Stack>
</Card>}
</div>
);
}
I have added a button which should close the entire Card component. If I add the following code
<CloseIcon onClick={() => setFileName(false)}/>
If I add the following code, then the component closes. But the next time you select a file, this component does not open (only after reloading the page).
Tell me how to close the Card component correctly so that you can open it without problems
I would suggest to handle separately the card visibility and the file name value.
Something like this should work:
import React, { useState, useCallback } from "react";
const DisplaySelectedFile = () => {
const [fileName, setFileName] = useState(null);
const [showCard, setShowCard] = useState(false);
const handleSelectFile = useCallback(
(file) => {
setFileName(file);
file && setShowCard(true);
},
[setFileName, setShowCard]
);
const handleCloseCard = useCallback(() => {
setShowCard(false);
setFileName(null); // add this line only if it fits your use case
}, [setFileName, setShowCard]);
return (
<div>
<SelectFileButton setFileName={handleSelectFile} />
{showCard && (
<Card sx={styles.CommonStyle}>
<Stack
spacing={10}
direction="row"
style={{ paddingTop: "20px", paddingLeft: "10px" }}
>
<div>{fileName}</div>
<Stack spacing={3} direction="row">
<div>Convert to</div>
<ConvertToFormatFile></ConvertToFormatFile>
</Stack>
<Button>CONVERT</Button>
<CloseIcon onClick={handleCloseCard} />
</Stack>
</Card>
) || null}
</div>
);
}
export default DisplaySelectedFile;
I am making an app based off Nasa's api that shows a picture or video each day. I have also created a like button that toggles if a person clicks it to like a picture or to toggle it back to default. I created the app in a way where a user can select a date using a calendar component that is through material-ui and when they select a date, it will bring the picture or video from that date. I am trying to figure out how to attach the like button to each picture, so that if they like one picture, they can go to another day and it will let them like another picture. Currently, for example, if I like today's picture, select the calendar and go to yesterday's picture, my like button is still toggled. Here is the code I used for getting the pictures and other information to show:
export default function PictureOfDay(props) {
const [picture, setPicture] = useState([])
const {date} = props;
let selectDate = moment(date, "ddd MMM DD YYYY HH:mm:ss Z-HHmm");
let formatDate = selectDate.format("YYYY-MM-DD");
useEffect(() => {
axios.get(
`https://api.nasa.gov/planetary/apod?api_key=QQpTYaQHDUvPAyVorMgxfKhQEoSQikBYt5WuFCf6&date=${formatDate}`
)
.then((response) => {
console.log(response.data);
setPicture(response.data);
})
.catch((err) => {
console.log(err)
});
}, [formatDate]);
return (
<div className="info">
{props.fetchingData && (
<div className="key spinner">
<Loader type="Puff" color="#800080" height="60" width="60" />
<p>Loading Data</p>
</div>
)}
<PictureInfo
date={picture.date}
title={picture.title}
image={picture.url}
media={picture.media_type}
explanation={picture.explanation}
/>
</div>
);
}
I also have my Like button logic on a different component. Right now, it is not attached to the pictures and I am unsure how to go about doing that:
import React, {useState, useEffect} from "react";
import ToggleHearts from "./ToggleHearts"
import {
Button,
Typography,
} from "#mui/material";
import {makeStyles} from "#mui/styles";
const useStyles = makeStyles((theme) => ({
button: {
...theme.typography.buttons,
fontFamily: "Orbitron",
padding: "0.7em",
fontSize: "1.5em",
width: "3.2em",
height: "3.2em",
background: theme.palette.primary.mainGradient,
border: "2px solid pink",
"&:hover": {
background: theme.palette.secondary.mainGradient,
},
},
}));
const Likes = () => {
const classes = useStyles();
const [liked, setLiked] = useState(false)
const handleChangeHeart = () => {
setLiked((previousHeart) => {
return !previousHeart;
});
};
useEffect(() => {
setLiked(JSON.parse(window.localStorage.getItem("liked")));
}, [])
useEffect(() => {
window.localStorage.setItem('liked', liked);
}, [liked])
return (
<Button
className={classes.button}
variant="contained"
onClick={handleChangeHeart}
>
<Typography style={{ fontFamily: "Orbitron" }}>
<ToggleHearts liked={liked}/>
</Typography>
</Button>
);
}
export default Likes
The Likes component is just attached to my component that renders everything on the page, here is the code to that:
export default function PictureInfo(props) {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(true);
const handleClose = () => setOpen(false);
const newDate = moment(props.date).format("dddd, MMMM, Do YYYY");
return (
<React.Fragment>
<Grid
container
direction="row"
justifyContent="center"
alignItems="center"
>
<Grid item className={classes.formGridItem}>
<Typography variant="h2">Date: {newDate}</Typography>
</Grid>
<Grid item className={classes.formGridItem}>
<Typography variant="h2">Title of Photo: {props.title}</Typography>
</Grid>
</Grid>
<Grid
container
direction="column"
justifyContent="center"
alignItems="center"
>
<Grid item className={classes.formGridItem}>
{props.media === "video" ? (
<iframe
className={classes.image}
title="video"
src={props.image}
width="800px"
height="600px"
></iframe>
) : (
<img className={classes.image} alt="nasa" src={props.image} />
)}
</Grid>
<Likes />
<Grid item className={classes.formGridItem} align="center">
<Button
className={classes.button}
variant="contained"
onClick={handleOpen}
>
Click here for more information!
</Button>
</Grid>
<Modal
open={open}
onClose={handleClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<Box sx={style}>
<Typography
id="modal-modal-title"
style={{
borderBottom: "1px solid pink",
fontFamily: "'Orbitron', sans-serif",
marginBottom: "1em",
fontSize: "1.4em",
}}
>
Explanation of: {props.title}
</Typography>
<Box
style={{
background: "white",
border: "1px solid purple",
padding: "2em",
}}
>
<Typography
id="modal-modal-description"
variant="subtitle2"
style={{ fontSize: "1.2em" }}
>
{props.explanation}
</Typography>
</Box>
</Box>
</Modal>
<footer>
<Typography
variant="subtitle2"
style={{ fontSize: "1rem", color: "black" }}
>
©2021 Charlene Johnson
</Typography>
</footer>
</Grid>
</React.Fragment>
);
}
If i right to understand you
all picture or video posts, must have an unique ID in you data represent, you data for this must look like this for example:
[{
id:'id of post',
likes:[idOfUsers],
...other needed for you data
}]
So, now you can check for likes id of user, and toggle you 'like button', if ID of user exist in this post into a 'likes' array,
or store IDs of posts to 'postsWhichwasLiked':[idsOfPosts],
and then to define which of posts was liked across getting id from this array and then to comparing with ID of post
i have tried to center it using flexbox justify-content, but it didn't work
Then I tried by replacing the Typography component with a div tag, still, it didn't work
import {AppBar,Zoom,Toolbar,Typography,CssBaseline,useScrollTrigger,Fab,makeStyles,IconButton,Container } from '#material-ui/core'
import PropTypes from 'prop-types';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
import styles from './menu.module.css'
import MenuIcon from '#material-ui/icons/Menu'
const useStyles = makeStyles(theme => ({
root: {
position: "fixed",
bottom: theme.spacing(2),
right: theme.spacing(2)
}
}));
function ScrollTop(props) {
const { children, window } = props;
const classes = useStyles();
// Note that you normally won't need to set the window ref as useScrollTrigger
// will default to window.
// This is only being set here because the demo is in an iframe.
const trigger = useScrollTrigger({
target: window ? window() : undefined,
disableHysteresis: true,
threshold: 100
});
const handleClick = event => {
const anchor = (event.target.ownerDocument || document).querySelector(
"#back-to-top-anchor"
);
if (anchor) {
anchor.scrollIntoView({ behavior: "smooth", block: "center" });
}
};
return (
<Zoom in={trigger}>
<div onClick={handleClick} role="presentation" className={classes.root}>
{children}
</div>
</Zoom>
);
}
// ScrollTop.propTypes = {
// children: PropTypes.element.isRequired,
// /**
// * Injected by the documentation to work in an iframe.
// * You won't need it on your project.
// */
// window: PropTypes.func
// };
export default function BackToTop(props) {
return (
<React.Fragment>
<CssBaseline />
<AppBar>
<Toolbar>
<IconButton>
<MenuIcon
className={styles.icon}
edge="end"
color="inherit"
aria-label="menu"
/>
</IconButton>
<Typography align='center' variant="h5">
Información
</Typography>
</Toolbar>
</AppBar>
<Toolbar id="back-to-top-anchor" />
<Container>
<Typography></Typography>
</Container>
<ScrollTop {...props}>
<Fab color="secondary" size="small" aria-label="scroll back to top">
<KeyboardArrowUpIcon />
</Fab>
</ScrollTop>
</React.Fragment>
);
}}```
The problem is probably Toolbar does not center its children. You could do something like
const useStyles = makeStyles(theme => ({
root: {
position: "fixed",
bottom: theme.spacing(2),
right: theme.spacing(2)
},
toolBar: {
display: "flex",
justifyContent: "center",
alignItems: "center",
}
}));
and use it
<Toolbar className={classes.toolBar}>
<IconButton>
<MenuIcon
className={styles.icon}
edge="end"
color="inherit"
aria-label="menu"
/>
</IconButton>
<Typography align='center' variant="h5">
Información
</Typography>
</Toolbar>
To make sure the typography is centered all the time, you can wrap it with the component. Because Container always centers it's children, the typography will be centered as a result of that. Like this
<Container>
<Typography variant="h5">Información</Typography>
</Container>
I have a tab component in Material-UI and I want to implement a tooltip on it.
My problem is that when I click the tab component, the tooltip is not disappearing. It must disappear after I click on that tab.
Currently, it continues to be visible even after I click on the tab.
How do I rectify that?
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ToolTip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ToolTip>
);
}
)}
</Tabs>
If you look at the document of Material-UI tooltip API
You would find a props named disableHoverListener
bool
default: false
Do not respond to hover events.
Set it as True would turn off the tooltip onMouseOver event trigger.
Update
Or you can simply make it totally under control.
By binding the onClick, onMouseOver, onMouseLeave, open to related component.
import React, { useState } from "react";
import "./styles.css";
import { Tooltip, Tab } from "#material-ui/core";
export default function App() {
const [flg, setFlg] = useState(false);
const [isHover, setIsHover] = useState(false);
return (
<div className="App">
<Tooltip
title={"message"}
aria-label="add"
placement="bottom"
open={!flg && isHover}
>
<Tab
label={`Click: ${!flg ? "enabled" : "disabled"}`}
onClick={() => setFlg(!flg)}
onMouseOver={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
/>
</Tooltip>
</div>
);
}
Try it online:
You can also implement a generic tooltip with a managed state when to open/close the tooltip.
import Tooltip, { TooltipProps } from "#mui/material/Tooltip";
import { useState } from "react";
/**
* MUI Tooltip wrapper with adaption to the move away once focuses left.
*/
export function ManagedTooltip(props: TooltipProps) {
const [open, setOpen] = useState<boolean>(false);
// Wrap Tooltip with div to capture mouse events
return <div style={{ display: 'flex' }}
onMouseEnter={() => setOpen(true)}
onMouseLeave={() => setOpen(false)}
onClick={() => setOpen(false)}
>
{/* Show the original MUI Tooltip with all props. */}
{/* Just override the open attribute to be fully managed, and disable internal listeners */}
<Tooltip {...props} open={open} disableHoverListener disableFocusListener />
</div>;
}
Once it's ready, you can use it anywhere exactly like the original MUI tooltip.
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ManagedTooltip description={description}>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ManagedTooltip>
);
}
)}
</Tabs>
The way I solved this was by rendering the tooltip conditionally. In your case I suppose you want the tooltip not to render for the tab of the current active route:
function ConditionalTooltip({renderTooltip, children, ...props}) {
return renderTooltip ? <Tooltip {...props}>{children}</Tooltip> : children;
}
function Tabs() {
const location = useLocation();
return (
<Tabs
className="navbar-routes"
value={value}
style={{ color: 'green'}}
indicatorColor="secondary"
onChange={handleChange}
>
{
tabsData.map(({id,title,description}) => {
return(
<ConditionalTooltip
renderTooltip={location.pathname.indexOf(title) === -1} /* only render tooltip on not active urls */
title={description}
>
<Tab
style={{
minWidth: 10,
fontSize: '80%',
fontWeight: 'bold',
marginLeft: '-4px',
marginRight: 4
}}
key={id}
component={Link}
to={`/${title}`}
label={`${title}`}
/>
</ConditionalTooltip>
);
}
)}
</Tabs>
)
}
I'm using a React/MUI Popover inside a react-window List element and am unable to get the Popover to position correctly -- it always winds up in the top left corner of the window (the component is unable to perform a getBoundingClientRctd() on the anchor element [anchorEl in the docs]).
So to get around that problem temporarily, I decided to use the anchorPosition parameter which allows to set an absolute position -- in my case, just the middle of the window. That's not working either.
I've reviewed the values in Chrome DevTools and everything seems to be OK (i.e., I do get an anchorEl when I'm using that; I get valid positionLeft/Top values; etc...
Probably something really simple and hoping someone can point out what I did wrong.
Edited: Key elements of the solution
Row component must be defined outside of the containing component.
the <List> component has an itemData attribute which is used to pass custom data to Row.
Edited to add react-window List renderer.
Here's the basic setup:
Popover renderer
renderPopover(template, itemId) {
const { anchorEl, anchorId } = this.state;
const { classes } = this.props;
const open = Boolean(anchorEl) && anchorId === itemId;
const bgColor = '#babaef';
const { innerHeight, innerWidth } = window;
const positionLeft = innerWidth / 2;
const positionTop = innerHeight / 2;
console.log(`renderPopover: ${positionLeft} / ${positionTop}`);
<Popover
id="simple-popper"
open={open}
style={{ color: 'Black' }}
anchorEl={anchorEl}
onClose={event => this.handlePopoverClose(event)}
anchorPosition={{ left: {positionLeft}, top: {positionTop} }}
anchorReference="anchorPosition"
>
<Typography style={{ backgroundColor: bgColor }} className={classes.typography}>
{this.renderScheduleElements(template, itemId)}
</Typography>
</Popover>
);
}
Button element renderer
renderScheduleComponent(template, itemId) {
const { anchorEl, anchorId } = this.state;
const open = Boolean(anchorEl) && anchorId === itemId;
const { classes } = this.props;
const id = open ? 'simple-popper' : undefined;
return (
<Grid key={itemId} item>
<Paper className={classes.paper}>
<div style={{ padding: '4px' }}>
<Button
NO_ref={itemId}
NODE_ref={(node) => this.buttonRef = node}
id={itemId}
name={itemId}
aria-owns={id}
aria-haspopup="true"
variant="outlined"
color="primary"
style={{
fontWeight: 'bold',
padding: '8px',
margin: 'auto',
display: 'block',
width: '100%',
}}
onClick={event => this.handlePopoverClick(event, itemId)}
>
{template.templateName}
</Button>
{(this.renderPopover).call(this, template, itemId)}
</div>
</Paper>
</Grid>
);
}
Click event handler
handlePopoverClick(event, id) {
event.preventDefault();
console.log(`handlePopoverClick : ${event.currentTarget.name}`);
this.setState({
anchorEl: event.currentTarget,
anchorId: id,
});
}
react-window List renderer
renderScheduleColumn(columnData) {
const { classes } = this.props;
const { scheduleDate, scheduleTemplates } = columnData;
this.scheduleTemplates = scheduleTemplates;
const Row = ({ index, style }) => {
return (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
{this.renderScheduleComponent(scheduleTemplates[index], `${scheduleDate}:${index}`)}
</div>
);
}
const { columnHeight, columnWidth } = this.state;
return (
<Grid id={scheduleDate} key={scheduleDate} item>
<Paper className={classes.paper}>
<div style={{ width: '100%', textAlign: 'center' }}>
<Typography variant="h6" style={{ padding: '24px', color: 'white', backgroundColor: '#3f51b5' }}>
{scheduleDate}
</Typography>
</div>
<List
className="List"
height={columnHeight}
itemCount={scheduleTemplates.length}
itemSize={50}
width={columnWidth}
>
{Row}
</List>
</Paper>
</Grid>
);
}
It looks like a similar problem as here: React Material-UI menu anchor broken by react-window list.
You should move the definition of your Row function out of renderScheduleColumn so that it is a consistent type. This will require moving/reworking renderScheduleComponent as well. You can use the itemData property on the List to pass information to the Row.