Skip to content

[core] Use router specific Link components - #4661

Merged
bharatkashyap merged 11 commits into
mui:masterfrom
bharatkashyap:fix/router-specific-link
Feb 24, 2025
Merged

[core] Use router specific Link components#4661
bharatkashyap merged 11 commits into
mui:masterfrom
bharatkashyap:fix/router-specific-link

Conversation

@bharatkashyap

@bharatkashyap bharatkashyap commented Feb 5, 2025

Copy link
Copy Markdown
Collaborator

@bharatkashyap bharatkashyap added the internal Behind-the-scenes enhancement. Formerly called “core”. label Feb 5, 2025
@mui-bot

mui-bot commented Feb 5, 2025

Copy link
Copy Markdown

Netlify deploy preview

https://deploy-preview-4661--mui-toolpad-docs.netlify.app/

Generated by 🚫 dangerJS against ecbf7e3

Comment on lines +5 to +6
import { LinkProps as ReactRouterLinkProps } from 'react-router';
import { LinkProps as NextLinkProps } from 'next/link';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems incorrect because:

  • the package should never import from third party runtimes
  • I don't think the implementations are 100% compatible

We need to define an abstract LinkProps type ourselves, and then make sure in the routeradapter to map the concrete implementation to the abstract properties.

*/

export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
export interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {

@Janpot Janpot Feb 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change this type. The router adapters are responsible for translating this to their concrete implementation. The goal is for us internally have only one standard way to create links, and the router adapter to translate this to a specific implementation.

ref: React.ForwardedRef<HTMLAnchorElement>,
) {
const { children, href, onClick, history, ...rest } = props;
const { children, href, onClick, history, to, ...rest } = props;

@Janpot Janpot Feb 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { children, href, onClick, history, to, ...rest } = props;
const { children, href, onClick, history, ...rest } = props;

We're just passing href internally. Remap correctly to to in the react-router adapter instead.

pathname: string;
searchParams: URLSearchParams;
navigate: Navigate;
Link?: React.JSXElementConstructor<LinkProps>;

@Janpot Janpot Feb 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Link?: React.JSXElementConstructor<LinkProps>;
Link?: React.ComponentType<LinkProps>;

I don't think we want to allow for say 'div' or something

}, [routerContext, onClick, history]);

return (
return routerContext?.Link && href ? (

@Janpot Janpot Feb 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there is a Link component it should always be used. Maybe it needs to be able to accept an optional href? Or you need to adjust the types of the Link component?

@Janpot Janpot Feb 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, the Link component that is exported from this module should have the same signature as the one in the routerContext. Ideally the current implementation is renamed to DefaultLink and we export a new version that wraps it:

export const Link = React.forwardRef(function Link(
  props: LinkProps,
  ref: React.ForwardedRef<HTMLAnchorElement>,
) {
  const routerContext = React.useContext(RouterContext);
  const LinkComponent = routerContext?.Link ?? DefaultLink
  return <LinkComponent ref={ref} {...props} />
})

this to avoid unnecessary callback creation and other render logic running in the default link.

*/

export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
export interface DefaultLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't this just be the LinkProps that we use both for DefaultLink and Link?

Suggested change
export interface DefaultLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {

import { LinkProps } from '../shared/Link';

const Link = React.forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const { href, ...rest } = props;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this should also handle the history prop?

pathname,
searchParams,
navigate,
Link,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this needs to handle the history prop?

@Janpot Janpot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great now. Maybe some tests?

@bharatkashyap

Copy link
Copy Markdown
Collaborator Author

Looks great now. Maybe some tests?

I think we can add effective tests for this only if we set up and run an actual app (avoid mocking), so I think we can take in a follow-up to this? I'll merge this and create an item for myself to do that

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

Labels

internal Behind-the-scenes enhancement. Formerly called “core”.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Navigation links with locale Basename Not Included in href for Links in Left Navigation

3 participants