简体   繁体   中英

react-router-dom v6 NavLink and MUI ListItem not working with className

I have a problem using MUI with react-router-dom v6.

import ListItem from '@mui/material/ListItem';
import { NavLink } from 'react-router-dom';
<List key={index}>
  <ListItem
     component={NavLink}
     sx={{
       color: '#8C8C8C',
     }}
     to={'/home'}
     className={({ isActive }) => (isActive ? classes.activeLink : undefined)
     >
     <ListItemIcon></ListItemIcon>
   <ListItemText primary='Home'/>
 </ListItem>
</List>

className not working and show error:

No overload matches this call.
  The last overload gave the following error.
    Type '({ isActive }: { isActive: any; }) => boolean' is not assignable to type 'string'
The expected type comes from property 'className' which is declared here on type 'IntrinsicAttributes & { button?: false | undefined; } & ListItemBaseProps & { components?: { Root?: ElementType<any> | undefined; } | undefined; componentsProps?: { ...; } | undefined; } & CommonProps & Omit<...>'

Issue

The issue is that the className prop is for the ListItem , not the component you've specified to be rendered. It's not an extraneous prop and isn't passed along to the NavLink component.

Solution

The solution appears to be to create a custom navlink component with the dynamic className prop enclosed. Pass your dynamic className function on a different prop, say activeClassName , and pass this to the NavLink 's className prop internally.

Example:

import { NavLink as NavLinkBase } from 'react-router-dom'; 

const NavLink = React.forwardRef((props, ref) => (
  <NavLinkBase
    ref={ref}
    {...props}
    className={props.activeClassName}
  />
));

...

import ListItem from '@mui/material/ListItem';
import { NavLink } from '../path/to/NavLink';

...

<List key={index}>
  <ListItem
    component={NavLink}
    activeClassName={({ isActive }) =>
      isActive ? classes.activeLink : undefined
    }
    sx={{ color: '#8C8C8C' }}
    to="/home"
  >
    <ListItemIcon></ListItemIcon>
    <ListItemText primary='Home' />
  </ListItem>
</List>

编辑 react-router-dom-v6-navlink-and-mui-listitem-not-working-with-classname

Thanks all, and here is my solution

import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { Theme } from '@mui/material/styles';
import { createStyles, makeStyles } from '@mui/styles';
import React from 'react';
import { NavLink } from 'react-router-dom';

const MyNavLink = React.forwardRef<any, any>((props, ref) => (
  <NavLink
    ref={ref}
    to={props.to}
    className={({ isActive }) => `${props.className} ${isActive ? props.activeClassName : ''}`}
  >
    {props.children}
  </NavLink>
));

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    activeLink: {
      backgroundColor: '#19ABC0',
      color: '#FFFFFF',
      borderRadius: 8,
      '& .MuiSvgIcon-root': {
        color: '#FFFFFF',
        stroke: '#FFFFFF',
        fill: '#19ABC0',
      },
    },
  })
);

function Sidebar() {
  const classes = useStyles();

  return (
    <>
      <List>
        <ListItem
          component={MyNavLink}
          sx={{
            color: '#8C8C8C',
          }}
          to={'/home'}
          activeClassName={classes.activeLink}
        >
          <ListItemIcon sx={{ stroke: '#8C8C8C', fill: '#FFFFFF' }}></ListItemIcon>
          <ListItemText primary={'Home'} />
        </ListItem>
      </List>
      <List>
        <ListItem
          component={MyNavLink}
          sx={{
            color: '#8C8C8C',
          }}
          to={'/dashboard'}
          activeClassName={classes.activeLink}
        >
          <ListItemIcon sx={{ stroke: '#8C8C8C', fill: '#FFFFFF' }}></ListItemIcon>
          <ListItemText primary={'Dashboard'} />
        </ListItem>
      </List>
    </>
  );
}

className={({ isActive }) => `${props.className} ${isActive ? props.activeClassName : ''}`}

To use MUI's className and add NavLink's active class.

The className property in a ListItem can only accept a string However on a NavLink, you can define a function that has its state (active or inactive)

You can create a CustomNavLink component that gets the properties from the ListItemButton (using forwardRef) and also have different styles based on its state

If you want to preserve the styling of mui's ListItemButton make sure you add its appropriate mui css classname to the className property of the NavLink. You can then add the different styling classes for whether it's active or not.

const CustomNavLink = forwardRef((props, ref) => (
  <NavLink
    ref={ref}
    {...props}
    className={({ isActive }) => (isActive ? props.className + ' Mui-selected' : props.className)}
    end
  />
));

Make sure you use the CustomNavLink as the component for ListItemButton and add the to property which will be forwarded to the NavLink

<List>
  <ListItem>
    <ListItemButton
      component={CustomNavLink}
      to="/"
    >
      <ListItemIcon><ListIcon /></ListItemIcon>
      <ListItemText primary='Home' />
    </ListItemButton>
  </ListItem>
</List>

It's bothering me this couple of days.. and what I found to be the least complicated solution is to add;important. to the CSS of .active

 <Button component={NavLink} to="/home"> Home </Button>
 .active { color: #F07DEA;important; }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM