'React onClick routing ONLY works SOMETIMES

Here is the video proof. https://dsc.cloud/leonardchoo/Screen-Recording-2022-03-08-at-17.25.58.mov

I'm running into a mysterious error where I click the button for navigation, "onClick" event is fired but it does not redirect and render the target component.

As you can see in the screenshot, the onClick event is logged, but the redirect does not happen.

I reproduced the situation here in CodeSandbox.

Stack

  • React TS
  • Mantine UI
  • React Router V5

How can I solve this issue?

screenshotError



Solution 1:[1]

First thing I noticed in your code was that is is rendering a WrapperPage component around each routed component with the navigation logic. I tried simplifying the WrapperPage code as much as possible.

Steps Taken:

  1. Refactored the header and navbar props into standalone components in case there was issue generating JSX
  2. Wrapped the Switch component in App with a single WrapperPage instead of each routed component

The issue persisted.

I next removed the UnstyledButton from @mantine/core so only the Link components were rendered, and could not reproduce. I then tried vanilla HTML buttons instead of the UnstyledButton and they again reproduced the issue.

So it seems it is an issue with rendering an interactive element (i.e. anchor tag from Link) within another interactive element (i.e. button from UnstyledButton) that is an issue. Swapping the element order, i.e. Link wrapping the UnstyledButton, appears to reduce the issue. I can't seem to reproduce the issue with the DOM structured this way.

Header

const CustomHeader = ({
  opened,
  setOpened
}: {
  opened: boolean;
  setOpened: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
  const theme = useMantineTheme();

  return (
    <Header height={70} padding="md">
      {/* Handle other responsive styles with MediaQuery component or createStyles function */}
      <div style={{ display: "flex", alignItems: "center", height: "100%" }}>
        <MediaQuery largerThan="sm" styles={{ display: "none" }}>
          <Burger
            opened={opened}
            onClick={() => setOpened((o) => !o)}
            size="sm"
            color={theme.colors.gray[6]}
            mr="xl"
          />
        </MediaQuery>

        <Group>
          <ThemeIcon variant="light" color="orange">
            ?
          </ThemeIcon>
          <Text>Mantine AppShell with React Router</Text>
        </Group>
      </div>
    </Header>
  );
};

Navbar

const CustomNavbar = ({ opened }: { opened: boolean }) => {
  const location = useLocation();
  const { classes } = useStyles();

  return (
    <Navbar
      padding="md"
      // Breakpoint at which navbar will be hidden if hidden prop is true
      hiddenBreakpoint="sm"
      // Hides navbar when viewport size is less than value specified in hiddenBreakpoint
      hidden={!opened}
      // when viewport size is less than theme.breakpoints.sm navbar width is 100%
      // viewport size > theme.breakpoints.sm – width is 300px
      // viewport size > theme.breakpoints.lg – width is 400px
      width={{ sm: 300, lg: 400 }}
    >
      <Link
        to="/dashboard"
        className={classes.link}
      >
        <UnstyledButton
          className={
            location.pathname === "/dashboard"
              ? classes.button_active
              : classes.button
          }
        >
          <Group>
            <ThemeIcon variant="light">
              <DashboardIcon />
            </ThemeIcon>
            <Text size="sm">Dashboard</Text>
          </Group>
        </UnstyledButton>
      </Link>
      <Link
        to="/new-recording"
        className={classes.link}
      >
        <UnstyledButton
          className={
            location.pathname === "/new-recording"
              ? classes.button_active
              : classes.button
          }
        >
          <Group>
            <ThemeIcon variant="light" color="red">
              <RadiobuttonIcon />
            </ThemeIcon>

            <Text size="sm">New Recording</Text>
          </Group>
        </UnstyledButton>
      </Link>
      <Link
        to="/calendar"
        className={classes.link}
      >
        <UnstyledButton
          className={
            location.pathname === "/calendar"
              ? classes.button_active
              : classes.button
          }
        >
          <Group>
            <ThemeIcon variant="light" color="orange">
              <CalendarIcon />
            </ThemeIcon>

            <Text size="sm">Calendar</Text>
          </Group>
        </UnstyledButton>
      </Link>
    </Navbar>
  );
};

WrapperPage

const WrapperPage = ({ children }: Props): JSX.Element => {
  const [opened, setOpened] = useState(false);

  return (
    <AppShell
      // navbarOffsetBreakpoint controls when navbar should no longer be offset with padding-left
      navbarOffsetBreakpoint="sm"
      // fixed prop on AppShell will be automatically added to Header and Navbar
      fixed
      header={<CustomHeader opened={opened} setOpened={setOpened} />}
      navbar={<CustomNavbar opened={opened} />}
    >
      {children}
    </AppShell>
  );
};

Edit react-onclick-routing-only-works-sometimes

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Drew Reese