-
-
Notifications
You must be signed in to change notification settings - Fork 428
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(sidebaritem): fix sidebar item with next link #438
Conversation
Codecov ReportBase: 97.41% // Head: 97.42% // Increases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #438 +/- ##
=======================================
Coverage 97.41% 97.42%
=======================================
Files 127 127
Lines 5460 5471 +11
Branches 443 443
=======================================
+ Hits 5319 5330 +11
Misses 141 141
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
I'm not sure about this one... maybe @tulup-conner could give it a look. |
I don't totally understand what this does TBH. I don't really use Next. I don't think it makes sense for the library to have Next-specific patches. |
Yes. I agree with you one this one @tulup-conner, I don't want to see specific-nextjs or any specific stuff landing at the flowbite-react. I have the same feeling that this is too specific. I'm wondering if #430 can't be solved in some more generic approach. |
Also, I'm seeing the "Legacy" word there... So, is this for new Next.js version or just to support oldest versions? I mean, if this is a legacy support, I'll say: we don't care. |
@@ -0,0 +1,15 @@ | |||
import { useCallback, useState, type FC, type ReactElement } from 'react'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component seems to be like a "documented solution" not a flowbite-react component. So, maybe we could add it to the documentation insted? Some "Known Issues" section?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This component is only to test the behavior. But testing it is important, because the anti-pattern (component in component) is not obvious to new React devs.
return ( | ||
<ListItem> | ||
<ListItem id={id} isCollapsed={isCollapsed} contentChildren={children}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I felt like having the same id
there and at line 144 will be a problem. I mean, the id
should be always unique. Right? Maybe adding some suffix to the one at 144 line, something like ${id}-children
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id
!= id
. This will be passed down to Children
not printed as html. But i also feel like it's complicated to follow the code. I haven't changed the code, just moved it out of the component.
I changed:
const ComponentA: FC = (props) => {
const ComponentB: FC = () => {
// ...
};
// ...
};
to
const ComponentB: FC = () => {
// ...
};
const ComponentA: FC = (props) => {
// ...
};
But yes, now it looks weird because of id
and contentChildren
etc.. But i guess, let's not make to much changes and concentrate us on fixing the bug first. 😄
@rluders @tulup-conner We don't need it to be named NextLinkLegacyBehavior, but currently it's happening to next's Link component. The reason i added this test, was to ensure, no one, creates components inside components, like this: const ComponentA: FC = (props) => {
const ComponentB: FC = () => {
// ...
};
// ...
}; Because it's an anti-pattern, but it's not that obvious. Currently it causes this infinite rerenderings with the Next Link button. But it can also happen to other frameworks like Gatsby if they use same approaches. I see, that NextLinkLegacyBehavior is not the best naming. This can be renamed ( My suggestion would be something like this: import { useState, useCallback, type FC } from 'react';
export interface UnstableReferenceTestProps {
maxUpdateDepth?: number;
children: (setRef: (ref: unknown) => void) => JSX.Element;
}
export const UnstableReferenceTest: FC<UnstableReferenceTestProps> = ({
maxUpdateDepth = 10,
children
}) => {
const [count, setCount] = useState(0);
const setRef = useCallback(
() => {
if (count >= maxUpdateDepth) {
throw new Error('Maximum update depth exceeded.');
} else {
setCount((x) => x + 1);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
return children(setRef);
}; |
Been following this a bit as I am having the same issue reported in #397 using the vanilla { Link } from "react-router-dom" producing the Some of this conversation is pushing my boundaries of component rendering :) but just wanted to add a note that the issue doesn't appear to be isolated to the NextJs Link component. Not sure if there are any nuances with how NextJs extends from the core React lib or if it even does at all but I've noticed that the NextJs link shows to use
|
@rluders @tulup-conner So how shall we proceed? |
Well, I've brought this up somewhere on this repo before, but the universal standard for component libraries as far as I understand is to provide a prop, generally called <Sidebar.Item className="mb-2" icon={IoMdPin} as={Link} to="/">Map</Sidebar.Item> I tried to do this a few months ago but I'm too stupid. Or, perhaps I just need to try again. Anyway, I think this is probably the most familiar and ergonomic solution. |
I'm just wondering what would happen if the And all that I'm saying right now could be really dumb... I'm just wondering what could happen. 🤔 |
That makes sense to me, too. I have a theory if we could figure out how to do the |
@tulup-conner The interface FlowbiteLinkProps<P = {}> {
as: FC<P>;
props: P & JSX.IntrinsicAttributes;
}
function FlowbiteLink<P = {}>({ as: Component, props }: FlowbiteLinkProps<P>) {
return <Component {...props} />;
} And then be used like this: <FlowbiteLink as={CustomLink} props={{ href: '#' }} /> But i would prefer @rluders approach but with a |
I'm not sure if I would take my suggestion seriously. I have no idea how it is going to work and if it is a good practice at all. It was just a crazy idea. 🤣 I think that also using the |
Ok, I tried again and the issue I was having with Sidebar.Item is that you can't override className, which is how Remix facilitates self-aware links via NavLink. That's because we designate it ourselves in Sidebar.Item on L62. It looks like all other properties work just fine. I'm not sure how to work around this - it might be that I will just need to patch Remix on my apps. I'm not sure if it is something that will come up with other users/use cases where they need to override className for the as={} component and can't. |
I'm wondering if my crazy idea to use the Maybe we could give this crazy idea a try? To see what happens. WDYT? |
The workaround looks like... Patch to - className={classNames(
- theme.base,
- isActive && theme.active,
- !isCollapsed && isInsideCollapse && theme.collapsed.insideCollapse,
- className,
- )}
+ className={
+ typeof className === 'string' || typeof className === 'undefined'
+ ? classNames(
+ theme.base,
+ isActive && theme.active,
+ !isCollapsed && isInsideCollapse && theme.collapsed.insideCollapse,
+ className,
+ )
+ : className
+ } Custom component for ease of use export const SidebarNavLink: FC<NavLinkProps & SidebarItemProps> = function ({
children,
...props
}) {
const theme = useTheme().theme.sidebar.item;
return (
<FlowbiteSidebar.Item
as={NavLink}
className={({ isActive }: { isActive: boolean }) =>
classNames(theme.base, isActive && theme.active)
}
{...props}
>
{children}
</FlowbiteSidebar.Item>
);
}; Actual links <Sidebar.NavLink to="/brand/components/tooltip">Tooltip</Sidebar.NavLink> The end product in the final snippet here is an ideal DX from my perspective, but I think the pieces in between can be better. Overall, if this is all I have to write to accomplish this, it isn't really that bad. The custom component I made is for convenience. You can easily just write: <Sidebar.Item
as={NavLink}
to="/brand/components/tooltip"
className={({ isActive }) =>
classNames(theme.base, isActive && theme.active)
}
>
Tooltip
</Sidebar.NavLink> For example. |
@tulup-conner I appreciate your efforts, but I think we are currently doing too much effort for a small fix. Removing the React anti-pattern and moving the components outside the rendering cycle will solve the problem with Next.js. If the problem persists for other libraries, we should look at that separately. |
68dbea5
to
bb26a5d
Compare
@tulup-conner @rluders I removed most of the code. Now it only contains the relevant part. I moved |
@myasteiner Well, I guess that this now changes the PR general goal. So, maybe we could update the title and the description. |
@rluders I've updated the title and description of the PR. |
Description
This PR fixes the
next/link
component for theSidebarItem
.Fixes #430
Type of change
Checklist: