I'm using Link component from react-router-4 but the property _to_ is missing from material-ui Button component. I tried to use spread operator but then webpack is giving me error.
When I manually add _to_ inside "material-ui/index.d.ts" then it works.
<Button component={Link} to="/about">About</Button>
<Button component={Link} {...{ to: "/about" }}>About</Button>
It's unlikely any typing library will be smart enough to cover this use case. (I might be wrong still).
Ugly but this might work:
<Button component={Link} {...{ to: "/about" } as any} />
We can't really fix that other than allow every prop on ButtonBase
. But I am not sure how good the autocompletion of IDEs would be for the rest of the props. TS can not infer props allowd on the component passed to [component]
AFAIK.
My suggestion is to use the solution proposed by @strothj and wrap it in a LinkButton
component.
Just realized that I had webpack errors because I forgot to remove _Button_ with _to_ from on of my testing components. That's why I got errors.
Anyways this example:
<Button component={Link} {...{ to: "/about" }}>About</Button>
is now working. It's not pretty but it's working :)
Could we potentially add the component's type as a generic type parameter for ButtonBase (or use it implicitly), then imply the intersection of the button's and the component's prop types?
Anyways this example:
<Button component={Link} {...{ to: "/about" }}>About</Button>
is now working. It's not pretty but it's working :)
A little prettier approach:
to.tsx
import { LocationDescriptor } from 'history';
import { Link } from 'react-router-dom';
const to = (to: LocationDescriptor) => ({ to, component: Link as any });
export default to;
Then just do this:
import to from './to';
//...
<Button {...to("/page/1")} variant="flat">Click Here!</Button>
//...
Or write your own material button component:
interface ButtonLinkProps extends ButtonProps {
to: string
}
const ButtonLink = (props: ButtonLinkProps) => <Button {...props} component={({ innerRef, ...linkProps }) => <Link {...linkProps} to={props.to} />} >{props.children}</Button>;
And use this like a normal button:
<ButtonLink variant="outlined" color="primary" to="/" >Home</ButtonLink>
Above pattern is discouraged: See https://material-ui.com/guides/composition/#caveat-with-inlining for a detailed explanation.
You are right!
Again:
import React from "react";
import { Link } from "react-router-dom";
import Button, { ButtonProps } from "@material-ui/core/Button";
import { LocationDescriptor } from "history";
interface ButtonLinkProps extends ButtonProps {
to: LocationDescriptor
}
export class ButtonLink extends React.Component<ButtonLinkProps>{
public render() {
return (
<Button {...this.props} component={({ innerRef, ...linkProps }) => <Link {...linkProps} to={this.props.to} />} >
{this.props.children}
</Button>);
}
}
And use this like a normal button:
<ButtonLink variant="outlined" color="primary" to="/" >Home</ButtonLink>
I feel like this is a good use case for @ts-ignore
and a new component:
import * as React from 'react';
import { Link } from 'react-router-dom';
import Button, { ButtonProps } from '@material-ui/core/Button';
import { LocationDescriptor } from 'history';
interface ButtonLinkProps extends ButtonProps {
to: LocationDescriptor;
}
const ButtonLink = ({ to, ...props }: ButtonLinkProps) => {
// @ts-ignore: see https://github.com/mui-org/material-ui/issues/7877
return <Button component={Link} to={to} {...props} />;
};
export default ButtonLink;
This solution has no disadvantage:
Most helpful comment
Ugly but this might work:
<Button component={Link} {...{ to: "/about" } as any} />