How do I pass value to child - ReactNode?
Answered
Rhinelander posted this in #help-forum
RhinelanderOP
"use client";
export const WaitlistWrapper = ({ children }: { children: ReactNode }) => {
const { activeProject, setActiveProject } = useProjectContext();
if (!activeProject) {
return (
<ErrorCard title="Not selected" description="Not selected" />
);
}
return <div>{children}</div>;
};I have context that stores ID of active project. If there is no ID I return error but in children I want to access that ID of active project (to check if id exists in db + other stuff). How achieve that?
88 Replies
you cant analyze children... can you explain in more detail what you want to do with ids with the children + what the children are
@Rhinelander
@Rhinelander
RhinelanderOP
Okay
First: I have context that has waitlist ID
"use client";
import { Waitlist } from "@prisma/client";
import { createContext, ReactNode, useContext, useState } from "react";
type WaitlistContextProps = {
waitlistID: Waitlist["id"] | null;
setWaitlistID: (waitlist: Waitlist["id"] | null) => void;
};
const WaitlistContext = createContext<WaitlistContextProps | undefined>(
undefined,
);
export function WaitlistProvider({ children }: { children: ReactNode }) {
const [waitlistID, setWaitlistID] = useState<Waitlist["id"] | null>(null);
return (
<WaitlistContext.Provider value={{ waitlistID, setWaitlistID }}>
{children}
</WaitlistContext.Provider>
);
}
export function useWaitlistContext() {
const context = useContext(WaitlistContext);
if (context === undefined) {
throw new Error(
"useWaitlistContext must be used within a WaitlistProvider",
);
}
return context;
}I want to toggle between waiting lists and show data based on what is selected
"use client";
import { useWaitlistContext } from "@/context/waitlist";
import { ReactNode } from "react";
import { WaitlistErrorCard } from "./waitlist-error-card";
export const WaitlistWrapper = ({ children }: { children: ReactNode }) => {
const { waitlistID } = useWaitlistContext();
if (!waitlistID) {
return (
<WaitlistErrorCard title="Not selected" description="Not selected" />
);
}
return <div>{children}</div>;
};Code above is my page wrapper. So here I check if waitlistID exists if it doesn't I display error card
Now i want to pass that waitlist to children
childrens are my pages like analytics, settings etc...
there i want to check if that id exists in db
if it does i later display it
if it doesn't i'll show another error card
so you need to verify waitlistID exists in the db right?
RhinelanderOP
yeah
but pass that data to child
as i need to display data
why cant you just check it exists directly while fetching the project id itself?
one sec lemme check your context
yea see where you pass in the wiatlistID
@Rhinelander typescript
"use client";
import { useWaitlistContext } from "@/context/waitlist";
import { ReactNode } from "react";
import { WaitlistErrorCard } from "./waitlist-error-card";
export const WaitlistWrapper = ({ children }: { children: ReactNode }) => {
const { waitlistID } = useWaitlistContext();
if (!waitlistID) {
return (
<WaitlistErrorCard title="Not selected" description="Not selected" />
);
}
return <div>{children}</div>;
};
RhinelanderOP
I can not check for db in client component
where are you getting the waitlist id from itself
like is it from local storage or smthn?
RhinelanderOP
"use client";
...
type Props = {
waitlists: Waitlist[];
};
export const Combobox = ({ waitlists }: Props) => {
const [open, setOpen] = useState(false);
const { waitlistID, setWaitlistID } = useWaitlistContext();
const getWaitlistNameById = (id: string) => {
const waitlist = waitlists.find((w) => w.id === id);
return waitlist ? waitlist.name : null;
};
const handleSelect = (currentValue: string) => {
const newValue = currentValue === waitlistID ? null : currentValue;
if (newValue) {
setWaitlistID(newValue);
}
setOpen(false);
};
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="w-full justify-between"
>
{!waitlistID ? "Select Waitlist" : getWaitlistNameById(waitlistID)}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandEmpty>No waitlist found.</CommandEmpty>
<CommandList>
<CommandGroup>
{waitlists.map((waitlist) => (
<CommandItem
key={waitlist.id}
value={waitlist.id}
onSelect={(currentValue) => handleSelect(currentValue)}
>
{waitlist.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
waitlistID === waitlist.id ? "opacity-100" : "opacity-0",
)}
/>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};From this component
I'll add local storage later as i figure this out
hmm
RhinelanderOP
I think my whole aproach is wrong
This shit shouldn't be that complicated
yea what you can do
is make a server action or a route handler, and call it in a useEffect where you set it in context
RhinelanderOP
Give me a sec to try this
sure
its not the best idea yes.. ideally you do all this in server components, but since you plan on putting it in local storage.. better to check it at once in context directly
make sure to keep an empty dependency array
RhinelanderOP
"use client";
import { useWaitlistContext } from "@/context/waitlist";
import { Waitlist } from "@prisma/client";
import { Loader2 } from "lucide-react";
import { ReactNode, useEffect, useState } from "react";
import { WaitlistErrorCard } from "./waitlist-error-card";
export const WaitlistWrapper = ({ children }: { children: ReactNode }) => {
const { waitlistID } = useWaitlistContext();
const [waitlistData, setWaitlistData] = useState<Waitlist | undefined>(
undefined,
);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
if (waitlistID) {
const fetchWaitlist = async () => {
setLoading(true);
try {
const response = await fetch(`/api/waitlists/${waitlistID}`);
if (!response.ok) {
throw new Error(`Failed to fetch waitlist: ${response.statusText}`);
}
const data: Waitlist = await response.json();
setWaitlistData(data);
setError(null);
} catch (error: unknown) {
if (error instanceof Error) {
setError(error.message);
} else {
setError("An unexpected error occurred");
}
} finally {
setLoading(false);
}
};
fetchWaitlist();
}
}, [waitlistID]);
if (!waitlistID) {
return (
<WaitlistErrorCard title="Not selected" description="Not selected" />
);
}
if (loading) {
return (
<div className="flex h-full w-full items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div>
);
}
if (error) {
return <WaitlistErrorCard title="There was an error" description={error} />;
}
return (
<div>
{waitlistData?.name}
{children}
</div>
);
};I am lost
how do i pass that in
I mean it works fine but i still need to pass that to children
why do you want to pass it to children
its in context
your children have it (client ones)
if you want server components to also be able to access...your thing is just wrong
RhinelanderOP
import { WaitlistErrorCard } from "@/components/shared/waitlist-error-card";
import { WaitlistWrapper } from "@/components/shared/waitlist-wrapper";
import getUserSession from "@/lib/getUserSession";
import { getUserWaitlist } from "@/lib/prisma.queries";
import { Handshake, UserPlus, Users, Verified } from "lucide-react";
import { redirect } from "next/navigation";
import { CustomChart } from "./components/custom-chart";
import { StatCard } from "./components/stat-card";
export default async function AnalyticsPage() {
const session = await getUserSession();
const user = session?.user;
if (!user || !user.id) {
redirect("/authentication");
}
const waitlist = await getUserWaitlist(user.id);
if (!waitlist) {
return (
<WaitlistErrorCard title="Not found" description="add description" link />
);
}
return (
<WaitlistWrapper>
<div className="container space-y-6 py-8">
<h3>Analytics</h3>
<div className="grid gap-4 lg:grid-cols-2 xl:grid-cols-4">
<StatCard
label="Total Signups"
value={2135}
icon={<Users className="h-5 w-5 text-muted-foreground" />}
/>
<StatCard
label="Joined Via Refferals"
value={256}
icon={<Handshake className="h-5 w-5 text-muted-foreground" />}
/>
<StatCard
label="Verified Emails"
value={65}
icon={<Verified className="h-5 w-5 text-muted-foreground" />}
valueIsPercentage
/>
<StatCard
label="Joined Today"
value={31}
icon={<UserPlus className="h-5 w-5 text-muted-foreground" />}
/>
</div>
<CustomChart />
</div>
</WaitlistWrapper>
);
}you need to be able to convert it from client to server, the 2 ways to do that is 1) it being saved in a db you can fetch and get 2) push it into the url and then read it via searchparams
RhinelanderOP
this is the child
child shouldn't be client
cant you just call the context to get the project id lol?
you need to refactor at this point
what you shld do is save it in a db
RhinelanderOP
what do I save in db?
instead of using context and local storage
the project ID
RhinelanderOP
it is already saved i am getting it from db
.....so cant your server components get it from the db as well lol
why are you trying to pass it in to children
RhinelanderOP
its waitlist[] i need to know what to select
i want my url to be clean
if i am on /analytics and i switch between project url doesn't change
so i cant do the dynamic route
WAIT I GET IT
so there is a list of projects
the user selects a project from the menu
and then you show pages related to the id they selected
right?
RhinelanderOP
yeah.... but i have a bad news
RhinelanderOP
i refactored whole project to use [id]/...
Answer
RhinelanderOP
even worst thing i didn't commit to github before doing the change
thats what i would have done as well
RhinelanderOP
i am realllllly fu**ed
yeah
RhinelanderOP
but so much more work
yeah
you can check the file timeline
it shld have stuff saved even if you didnt commit
RhinelanderOP
yeah i see
thanks
yea
RhinelanderOP
going to try to change everything and keep you updated
RhinelanderOP
if it doesn't work ill just revert
sure
RhinelanderOP
I'll work on that tomorrow i changed like 80% of my code
RhinelanderOP
It works great now! I don't even know why I went the other route this is just as clean if not more plus 10x less code and 10x easier. Thank you for your help, hope i didn't waste too much time of yours