Material-ui: cannot use react-router Link with ListItem in TypeScript

Created on 12 Nov 2017  路  13Comments  路  Source: mui-org/material-ui

  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior


I should be able to somehow use a <ListItem> with a Link in TypeScript.

Current Behavior


I've found a few examples that suggest using:

<ListItem button component={Link} to="/somelink"></ListItem>

But I use TypeScript, and using to attr throws a compilation error since to is not a property of ListItem.

Steps to Reproduce (for bugs)

  1. Use create-react-app my-app --scripts-version=react-scripts-ts to create a simple react app with TS
  2. Add react-router as dep and install it
  3. Use a List in the app template and try to add an item with <ListItem button component={Link} to="/somelink"></ListItem>

Context


I'm trying to setup Material UI with react-router.

Your Environment

| Tech | Version |
|--------------|---------|
| Material-UI | next |
| React | 16.1.0 |
| browser | Chrome |
| etc | |

duplicate

Most helpful comment

@oliviertassinari ops, I haven't searched well enough 馃槃 But just to be clear, I should be using:

<ListItem component={props => <Link {...props} to="/about" />}>
  // ...
</ListItem>

Right? At least that's the only variation that works for me without throwing any TS compilation errors.

All 13 comments

It's something we have already been discussing in https://github.com/callemall/material-ui/issues/8063#issuecomment-342330737.

@oliviertassinari ops, I haven't searched well enough 馃槃 But just to be clear, I should be using:

<ListItem component={props => <Link {...props} to="/about" />}>
  // ...
</ListItem>

Right? At least that's the only variation that works for me without throwing any TS compilation errors.

@rolandjitsu Yes, I would encourage using this pattern.

@oliviertassinari thanks, I've just started with React and at this point all is a little confusing 馃槅

I think @rolandjitsu 's solution is the only way it could work becouse the ListItem 's typescript def. is fixed and adding Link can not be dyamnically changed.

@rolandjitsu This soultion no longer work in @types/react 16.0.36 version. :(

@activebiz I'll give that a try tomorrow and see if it still works for me.

It seems when ListItem's innerRef is passed to Link, Link will not work properly. Try this:

<ListItem component={({innerRef,...props}) => <Link {...props} to="/about" />} // ... </ListItem>

I wrote my own component for that which seams to work fine and is reasonably type safe:

import * as React from 'react'
import { ListItem } from '@material-ui/core';
import { Link } from 'react-router-dom';
import { ListItemProps } from '@material-ui/core/ListItem';
import { LocationDescriptor } from 'history';

interface Props extends ListItemProps {
    // ListItemProps and LinkProps both define an 'innerRef' property
    // which are incompatible. Therefore the props `to` and `replace` are
    // simply duplicated here.
    to: LocationDescriptor
    replace?: boolean
}

function createLink({innerRef, ...props}: Props) {
    // Remove `innerRef` from properties as the interface
    // is incompatible. The property `innerRef` should not be
    // needed as the `ListItem` component already provides that
    // feature with a different interface.
    return <Link {...props}/>
}

export class ListItemLink extends React.PureComponent<Props> {
    render() {
        return <ListItem {...this.props} component={createLink}/>
    }
}

I tried to write it as type safe as possible. That's the reason for a dedicated createLink function, so that the compiler can shout at me if LinkProps and ListItemProps become incompatible in the future.

Usage:

<ListItemLink to="/about">... </ListItemLink>

This solution is more or less identical to the one suggested by @adimitris but also adds some extra type safety (due to createLink) and wraps it all neatly together for a simpler usage.

The ripple effect seems to not be working when using @adimitris solution. Any idea why? I'm trying to figure out.

The ripple effect seems to not be working when using @adimitris solution. Any idea why? I'm trying to figure out.

@Nelrohd You need to create the link component outside of the render method. If you don't, the component reference will change at each render. React will consider it's a new component, unmount and remount it. You loose the ripple.

Same thing with Tab or any other component that can be a Link.

So far I found this:

<Tab label="Local" {...{component: Link, to: `/local`} as any}/>

Still an issue in 2020

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pola88 picture pola88  路  3Comments

chris-hinds picture chris-hinds  路  3Comments

reflog picture reflog  路  3Comments

anthony-dandrea picture anthony-dandrea  路  3Comments

iamzhouyi picture iamzhouyi  路  3Comments