Closed johnpwilkinson closed 3 months ago
Hi @johnpwilkinson,
Thanks for raising, I have not seen this before. Is there a change that the component which is using Kinde is being re-rendered for some reason in your project?
edit: On further review, this does not happen when navigating to /profile
... but does happen when navigating to my two other "main" sections of the app. The thing these two have in common is the rendering of a Table component that I made from scratch. The only references to Kinde that these tables have is at the top (server components), they both call getUser
and then take the users id
and pass it in to functions that generate the data for the table. That data is passed into the table and the table is rendered.
The table, is made up of a TableHeader
and one to N TableRow
components.... Each TableRow
has an actions
columns and this action column, is a server component called ActionIcons
that rendered 3 icons that serve as buttons for said row of data... (details, edit, delete)...
const ActionIcons = React.memo(({ mode, row }) => (
<div className="flex items-center justify-center gap-1 w-full h-full">
{mode !== "protocolDetail" && (
<div className="flex items-center justify-center">
<Link href={handleDetailsNav(mode, row.id)}>
<Search className="h-5 w-5" />
</Link>
</div>
)}
<div className="flex items-center justify-center">
<Modal
mode="edit"
data={row}
formFor={mode}
icon={<Pencil1Icon className="h-5 w-5" />}
>
<CustomForm />
</Modal>
</div>
<div className="flex items-center justify-center">
<Modal
formFor={mode}
data={row}
mode="delete"
icon={<Cross1Icon className="h-5 w-5" />}
>
<CardDialog />
</Modal>
</div>
</div>
));
ActionIcons.displayName = "ActionIcons";
export default ActionIcons;
you will notice that this component, uses the Modal
component so that when a user clicks on one of these action icons, a modal (or drawer depending on screen size) opens with the desired action or form...
lastly, and thank you for baring with me, Modal
is a client component that does call getUser
"use client";
import { useState, cloneElement, useEffect } from "react";
import { usePathname } from "next/navigation";
import { formData as myFormData } from "../lib/FormData";
import { useKindeBrowserClient } from "@kinde-oss/kinde-auth-nextjs";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer";
export default function Modal({ children, open, icon, mode, data, formFor }) {
const [isOpen, setIsOpen] = useState(open);
const [isDesktop, setIsDesktop] = useState(false);
const { getUser } = useKindeBrowserClient();
const pathname = usePathname();
useEffect(() => {
if (typeof window !== "undefined") {
const mediaQuery = window.matchMedia("(min-width: 768px)");
setIsDesktop(mediaQuery.matches);
const handleMediaQueryChange = (e) => setIsDesktop(e.matches);
mediaQuery.addEventListener("change", handleMediaQueryChange);
return () => {
mediaQuery.removeEventListener("change", handleMediaQueryChange);
};
}
}, []);
const user = getUser();
const handleClose = () => {
setIsOpen(false);
};
const handleOpen = () => {
setIsOpen(true);
};
const setFormFor = () => {
const [section, detail] = pathname.split("/").slice(1, 3);
if (section === "protocol") {
return {
section: detail === "overview" ? "protocolOverview" : "protocolDetail",
id: detail === "details" ? pathname.split("/")[3] : undefined,
};
} else if (section === "bloodwork") {
return {
section:
detail === "overview" ? "bloodworkOverview" : "bloodworkDetail",
id: detail === "details" ? pathname.split("/")[3] : undefined,
};
}
return { section: "" };
};
const recordId = data?.id;
const generateForm = (mode) => {
const routeInfo = setFormFor();
if (mode === "add") {
return {
content: { title: "add", id: routeInfo.id || "" },
formData: myFormData[routeInfo.section],
myFormFor: routeInfo.section,
};
} else if (mode === "edit") {
return {
content: { title: "edit" },
formData: myFormData[routeInfo.section],
myFormFor: formFor,
};
} else if (mode === "delete") {
return {
content: { title: "delete" },
formData: myFormData[routeInfo.section],
myFormFor: formFor,
};
}
return { content: {} };
};
const { content, formData, myFormFor } = generateForm(mode);
return isDesktop ? (
<Dialog>
<DialogTrigger>{icon}</DialogTrigger>
<DialogContent className="p-0 w-fit">
<DialogHeader>
<DialogTitle className="sr-only">{content.title}</DialogTitle>
<DialogDescription className="sr-only">
{content.id}
</DialogDescription>
</DialogHeader>
{cloneElement(children, {
handleClose,
mode,
data,
content,
formData,
formFor: myFormFor,
recordId,
user,
})}
</DialogContent>
</Dialog>
) : (
<Drawer open={isOpen} onOpenChange={setIsOpen} className="">
<DrawerTrigger>{icon}</DrawerTrigger>
<DrawerContent className="">
{cloneElement(children, {
handleClose,
mode,
data,
content,
formData,
formFor: myFormFor,
user,
})}
</DrawerContent>
</Drawer>
);
}
This is the only thing I can think of that would differentiate these two "main components" from the Profile component...
But would calling getUser
trigger the api/auth/setup
endpoint? Just doesnt make sense if so...
Hey @DanielRivers
thanks for the quick response.
Is there a change that the component which is using Kinde is being re-rendered for some reason in your project?
This is the thing I have been spending my time thinking about.
with the exception of the one client component, every other component that remotely uses kinde
is a server component. All of the code I have written, client or server, only ever uses the respective getUser
method from the Kinde SDK. (as noted above)
I guess, to fully elucidate if that is even a concern, I would ask the following....
Does calling getUser
from either the client or server side, in turn, make a GET request to app/auth/setup
?
or a better question, What triggers the app/auth/setup
route to be hit in the first place
I did not see any info that granular in the docs and I have not seen this issue mentioned elsewhere... not in youtube vids, tutorials, Nextjs docs/templates/repos.... and asking chatGPT about it is essentially a waste of time... as its canned responses are irrelevant to this particular situation.
With a better idea around what actually triggers the api/auth/setup
in the first place, perhaps I could then pinpoint where in my code the disconnect is happening.
A naive take, would be that the setup
endpoint is only hit when the user first logs in? If so, this is more interesting, as I see this request being made on every subsequent page load after they login.
another note, I had a protection strategy in place where I placed all protected routes into a (routeGroup)
called (protected)
and in this I have a layout.jsx:
import { LoginLink } from "@kinde-oss/kinde-auth-nextjs/components";
import { getKindeServerSession } from "@kinde-oss/kinde-auth-nextjs/server";
export default async function RootLayout({ children }) {
const { isAuthenticated } = getKindeServerSession();
return (await isAuthenticated()) ? (
<div className="">{children}</div>
) : (
<div>
This page is , please <LoginLink>Login</LoginLink> to view it
</div>
);
}
this works as far as protecting all of the children routes... I sorta cooked this idea up whilst learning about Next and using Kinde for the first time.
Earlier today, I removed the check on this component, rendered all children and then implemented the middleware to perform the authenticated
check.... and this works great too, in terms of protecting routes... but either way, the mass firing of the setup
endpoint is still taking place.
My hail mary idea was that for whatever reason, Kinde did not like that these routes were sitting behind a route group in the first place.... but I havent given that thought too much weight, as it just doesnt seem correct.
Here is the general folder structure of my project, maybe I am just missing something. Used Section
to obfuscate actual names of core parts of the app. Anything stick out as problematic?
.
├── README.md
├── app
│ ├── (protected)
│ │ ├── Section
│ │ │ ├── details
│ │ │ │ ├── [id]
│ │ │ │ │ └── page.jsx
│ │ │ │ └── page.jsx
│ │ │ ├── layout.jsx
│ │ │ ├── Section
│ │ │ │ ├── [id]
│ │ │ │ │ └── page.jsx
│ │ │ │ └── page.jsx
│ │ │ ├── overview
│ │ │ │ └── page.jsx
│ │ │ └── page.jsx
│ │ ├── Section
│ │ │ ├── Section
│ │ │ │ ├── details
│ │ │ │ │ └── page.jsx
│ │ │ │ └── overview
│ │ │ │ └── page.jsx
│ │ │ ├── Section
│ │ │ │ ├── details
│ │ │ │ │ └── page.jsx
│ │ │ │ └── overview
│ │ │ │ └── page.jsx
│ │ │ └── page.jsx
│ │ ├── Section
│ │ │ └── page.jsx
│ │ ├── layout.jsx
│ │ ├── lib
│ │ │ ├── FormData.jsx
│ │ │ ├── actions.js
│ │ │ ├── constants.js
│ │ │ ├── context
│ │ │ │ └── SomeContext.jsx
│ │ │ ├── data.js
│ │ │ ├── headers.js
│ │ │ ├── hooks
│ │ │ ├── placeholder-data.js
│ │ │ ├── prisma.js
│ │ │ └── utils.js
│ │ ├── profile
│ │ │ └── page.jsx
│ │ ├── Section
│ │ │ ├── details
│ │ │ │ ├── [id]
│ │ │ │ │ └── page.jsx
│ │ │ │ └── page.jsx
│ │ │ ├── layout.jsx
│ │ │ ├── overview
│ │ │ │ └── page.jsx
│ │ │ └── page.jsx
│ │ └── ui
│ │ ├── UIStuff.jsx
│ ├── api
│ │ ├── auth
│ │ │ ├── [kindeAuth]
│ │ │ │ └── route.js
│ │ │ └── addNewUser
│ │ │ └── route.js
│ │ └── webhook
│ │ └── route.js
│ ├── favicon.ico
│ ├── globals.css
│ ├── hooks
│ │ ├── useDevice.jsx
│ │ └── useScreenshot.jsx
│ ├── layout.jsx
│ ├── page.jsx
│ └── tools
│ └── page.jsx
├── components
│ ├── theme-provider.jsx
│ └── ui
│ ├── breadcrumb.jsx
│ ├── button.jsx
│ ├── calendar.jsx
│ ├── card.jsx
│ ├── chart.jsx
│ ├── dialog.jsx
│ ├── drawer.jsx
│ ├── dropdown-menu.jsx
│ ├── form.jsx
│ ├── input.jsx
│ ├── label.jsx
│ ├── popover.jsx
│ ├── select.jsx
│ ├── sheet.jsx
│ ├── slider.jsx
│ ├── switch.jsx
│ ├── tabs.jsx
│ └── tooltip.jsx
├── components.json
├── env.local
├── jsconfig.json
├── lib
│ ├── data.js
│ └── utils.js
├── middleware.js
├── next-env.d.ts
├── next.config.mjs
├── next.config.mjs.temp
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── prisma
│ ├── migrations
│ │ ├── 20240714173919_add_optional_end_date
│ │ │ └── migration.sql
│ │ ├── 20240714225049_
│ │ │ └── migration.sql
│ │ ├── 20240715194636_bloodwork_add
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ ├── schema.prisma
│ └── seed.js
├── public
│ ├── Logo.svg
│ ├── next.svg
│ └── vercel.svg
└── tailwind.config.js
@DanielRivers ... I think that just the act of responding to your comment and then tracing everything out mentally made all the difference...
After my response, it occurred to me that I shouldn't be calling getUser
inside of the client component Modal
regardless, when I could just pass the userId
from the top level server component.
Making this change has completely fixed the issue.
Thanks for being the proverbial rubber ducky
and thanks for the swift response and the ultimate resolution.
Kinde is fucking awesome!
Great that its all solved for you, pleased was able to solve your issue 🎉
Prerequisites
Describe the issue
I am seeing the
api/auth/setup
endpoint being hit 90 times on page loads after I successfully sign a user in to my Nextjs app.I did follow the docs and instruction.
I am getting the desired effect of being able to log a user in and authenticate that they are a user.
Here is my /app/api/[kindeAuth]/route.js
There are 5 server components in my code that call
getUser
fromgetKindeServerSession
in the same patternand one instance where this is being called from the client side with
useKindeBrowserClient
Thats it.... that is the extent of my employment of the Kinde SDK in this project.
This is happening in local dev and in the vercel deployed app.
I have tried to comment out / delete these invocations to see if somehow they were all being triggered with each page load.... did nothing... and furthermore, the more I look at the issue, the more I doubt that caling
getUser
has anything to do with it.Looking at the console as I log a user in and the initial page loads.... I see a series of actions:
/api/auth/setup - 200... they all resolve with 200s
This cannot be correct, right?
and I have been racking my brain to figure out what is causing this.... like I said,
getUser
doesnt seem to be the culprit, as these all take place immediately after logging in.I do not seem to be missing anything from the documentations set up steps...and the product is working n terms of auth and user management,
Here is a screenshot of some of them:
Library URL
https://github.com/kinde-oss/kinde-auth-react
Library version
2.3.5
Operating system(s)
macOS
Operating system version(s)
13.6
Further environment details
this is happening in local dev and in my vercel deploy app.
here is my package.json
My env variables should be correct... and the core functionality of Kinde does work as expected, so i do not think there is any issue there.
Using the devtools from chrome, the initiator is always code from the Kinde SDK, and I am only invoking the SDK with the
<LoginLink />
,<LogoutLink />
, and thegetUser()
. The clues from the network/devtools, do not really elucidate much more than that.Reproducible test case URL
No response
Additional information
No response