Home > Enterprise >  Factoring material UI popover into a higher order component and keep the popover state in the `hoc`
Factoring material UI popover into a higher order component and keep the popover state in the `hoc`

Time:12-26

I am following the react material ui doc here: https://mui.com/components/popover/, and I'm attempting to factor all the popover view and state logic into a higher order component. The one from the doc is here:




function BasicPopoverFromDoc(props) {

    const [anchorEl, setAnchorEl] = React.useState(null);

    const handlePopoverOpen = (event) => {
        setAnchorEl(event.currentTarget);
    };

    const handlePopoverClose = () => {
        setAnchorEl(null);
    };

    const open = Boolean(anchorEl);

    return (
        <div>
            <Typography
                aria-owns={open ? 'mouse-over-popover' : undefined}
                aria-haspopup="true"
                onm ouseEnter={handlePopoverOpen}
                onm ouseLeave={handlePopoverClose}
                style={{color:'white'}}
            >
                Hover with a Popover.
            </Typography>
            <Popover
                id="mouse-over-popover"
                sx={{
                  pointerEvents: 'none',
                }}
                open={open}
                anchorEl={anchorEl}
                anchorOrigin={{
                  vertical: 'bottom',
                  horizontal: 'left',
                }}
                transformOrigin={{
                  vertical: 'top',
                  horizontal: 'left',
                }}
                onClose={handlePopoverClose}
                disableRestoreFocus
            >
                <Typography sx={{ p: 1 }}>I use Popover.</Typography>
            </Popover>
        </div>
    );
}

I lifted all the state logic into a hoc as follows, taking <Child/> as a param:

function WithPopOver(props){


    const [anchorEl, setAnchorEl] = React.useState(null);

    const handlePopoverOpen = (event) => {
        setAnchorEl(event.currentTarget);
    };

    const handlePopoverClose = () => {
        setAnchorEl(null);
    };

    const open = Boolean(anchorEl);

    const { Child } = props;

    console.log('with Popover: ', open)


    return (
        <div>
            <Child 
                {...props}
                aria-owns={open ? 'mouse-over-popover' : undefined}
                aria-haspopup="true"
                onm ouseEnter={handlePopoverOpen}
                onm ouseLeave={handlePopoverClose}
            />
            <Popover
                id="mouse-over-popover"
                sx={{
                  pointerEvents: 'none',
                }}
                open={open}
                anchorEl={anchorEl}
                anchorOrigin={{
                  vertical: 'bottom',
                  horizontal: 'left',
                }}
                transformOrigin={{
                  vertical: 'top',
                  horizontal: 'left',
                }}
                onClose={handlePopoverClose}
                disableRestoreFocus
            >
                <Typography sx={{ p: 1 }}>I use Popover.</Typography>
            </Popover>
        </div>
    );
}

but when i try to use it this way:

function BasicPopover(props){

    return (
        <WithPopOver
            {...props}
            Child={() => <Typography style={{color:'white'}}> wrapped in hoc </Typography>}
        />
    )
}

It seems like the popover does not display. It refuses to display even when I change open to true by force. What am i missing here?

CodePudding user response:

You haven't passed Child props to it's children, so onMouseEnter and onMouseLeave never fires, try on this:

<WithPopOver
        {...props}
        Child={(props) => <Typography style={{color:'white'}} {...props}> wrapped in hoc </Typography>}
    />
  • Related