Skip to content

React Compiler interferes with reactive function auto-tracking #164

Description

@devuxer

Hey @pzuraq ,

Not sure if this is a bug or just me not understanding the nuances of the libraries involved here, but I wrote a helper function to manage the opening, closing, and anchoring of a Material UI Menu, and to my surprise it didn't work.

Here is the helper function (and the call to the function):

import { reactive, signal } from "signalium":

export function createMenuState() {
    const rawAnchorEl = signal<HTMLElement | null>(null);
    const anchorEl = reactive(() => rawAnchorEl.value);
    const canShow = reactive(() => !!anchorEl());

    return { anchorEl, canShow, open, close };

    function open(value: HTMLElement) {
        rawAnchorEl.value = value;
    }

    function close() {
        rawAnchorEl.value = null;
    }
}

export const sortMenuState = createMenuState();

Here is the menu component:

import { Check as CheckIcon } from "@mui/icons-material";
import { ListItemText, Menu, MenuItem } from "@mui/material";
import { component } from "signalium/react";
import { sortMenuState as state } from "./toolbar-state";

function SortMenu() {
    return (
        <Menu
            open={state.canShow()}
            anchorEl={state.anchorEl()}
            anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
            transformOrigin={{ vertical: "top", horizontal: "left" }}
            onClose={state.close}
        >
            <MenuItem onClick={state.close}>
                <CheckIcon />
                <ListItemText>Unit Name A-Z</ListItemText>
            </MenuItem>
            <MenuItem onClick={state.close}>
                <ListItemText>Unit Name Z-A</ListItemText>
            </MenuItem>
        </Menu>
    );
}

export default component(SortMenu);

Here is the button that spawns the menu:

import { FilterList as SortIcon } from "@mui/icons-material";
import { Button } from "@mui/material";
import { component } from "signalium/react";
import SortMenu from "./SortMenu";
import { sortMenuState } from "./toolbar-state";

function Toolbar() {
    return (
        <>
            <Button
                variant="text"
                startIcon={<SortIcon />}
                color="secondary"
                onClick={e => {
                    sortMenuState.open(e.currentTarget);
                }}
            >
                Sort
            </Button>
            <SortMenu />
        </>
    );
}

export default component(Toolbar);

If I look at the DOM in the dev tools, I see an expander very briefly appear for this element:

<span class="MuiTouchRipple-root css-r3djoj-MuiTouchRipple-root"></span>`

However, the menu itself never appears or appears so briefly that I cannot see it.

I added a bunch of console logs and confirmed that when I push the menu button, the value of the rawAnchorEl signal does indeed get set to the expected HTML element, and the SortMenu component does re-render. Even though the menu immediately disappears, though, rawAnchorEl never returns to null (i.e., the close mutation function never gets called).

I tried to use Anthropic Claude to debug this, and it "thought" the problem was something to do with MUI's use of React Portals:

MUI's Menu uses React portals and internal state management that might not play well with Signalium's reactive wrappers. The reactive() function might not be triggering the necessary React re-renders for the portal to mount.

If I ditch all the Signalium code and just follow MUI's example in the docs, which shows the anchor element being stored in a local useState, everything works fine.

Any insight on what could be going on here and if it's possible to fix this without reverting to useState?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions