Next.js Discord

Discord Forum

How should I restructure this?

Answered
Polar bear posted this in #help-forum
Open in Discord
Avatar
Polar bearOP
I currently have a layout for my dashboard that passes pages in children to a wrapper Dashboard component (I've made this component because it holds state of the sidebar that needs to be used both for sidebar and topbar because of buttons that allow users to collapse it if they want, and that makes them both client side components)

First question I have if it's okay to create such wrappers like I do (I was thinking about making it (the state for sidebar) in client side layout but I would lose the metadata)

Second question is if It's okay to fetch user profile like I do in <Profile /> in client component. I wanted it to be a server component at first but because my parent components are client side and the wrapper is too I would have to pass it from layout->dashboard wrapper->sidebar

(dashboard) / layout.tsx

import Dashboard from "@/components/ui/dashboard/Dashboard";
import { type Metadata } from "next";

export const metadata: Metadata = {
  title: "Dashboard",
};

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div className="flex h-screen w-full overflow-hidden">
      <Dashboard>{children}</Dashboard>
    </div>
  );
}

Dashboard.tsx

"use client";

import { useState } from "react";

import Sidebar from "./Sidebar";
import Topbar from "./Topbar";

export default function Dashboard({ children }: { children: React.ReactNode }) {
  const [isSidebarOpen, setIsSidebarOpen] = useState(true);
  return (
    <div className="flex h-screen w-full">
      <Sidebar
        isSidebarOpen={isSidebarOpen}
        setIsSidebarOpen={setIsSidebarOpen}
      />
      <div className="flex h-screen flex-1 flex-col">
        <Topbar
          isSidebarOpen={isSidebarOpen}
          setIsSidebarOpen={setIsSidebarOpen}
        />
        {children}
      </div>
    </div>
  );
}


Sidebar.tsx
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "../button";
import { LucideArrowLeft, LucideX } from "lucide-react";
import Profile from "./Profile";

type Props = {
  isSidebarOpen: boolean;
  setIsSidebarOpen: (isOpen: boolean) => void;
};

export default function Sidebar({ isSidebarOpen, setIsSidebarOpen }: Props) {
  const closeSidebar = () => setIsSidebarOpen(false);
  return (
    <AnimatePresence initial={false}>
      {isSidebarOpen && (
        <motion.div
          initial={{ width: 0 }}
          animate={{ width: 256 }}
          exit={{ width: 0 }}
          className="flex flex-col border-r overflow-x-hidden flex-shrink-0"
        >
          <div className="w-64 flex flex-shrink-0 flex-col">
            <div className="border-b h-14 px-4 items-center flex justify-between">
              <p className="font-medium tracking-wide text-sm font-mono">
                HawaldDB v0.0
              </p>
              <Button
                size="icon"
                variant="ghost"
                onClick={closeSidebar}
                className="size-8"
              >
                <LucideArrowLeft />
              </Button>
            </div>
            <Profile />
          </div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}


Profile.tsx
"use client";

import { createClient } from "@/utils/supabase/client";
import { useEffect, useState } from "react";
import { User } from "@supabase/supabase-js";

export default function Profile() {
  const [isLoading, setIsLoading] = useState(true);
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    const fetchUser = async () => {
      const supabase = createClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      setUser(user);
      setIsLoading(false);
    };

    fetchUser();
  }, []);

  if (isLoading) {
    return <div className="border-t px-4 flex h-14">Loading...</div>;
  }

  return (
    <div className="border-t px-4 flex h-14">
      {user?.email || "No user found"}
    </div>
  );
}
Answered by chisto
I made something similar with useQuery, prefetching user on Layout, you save it on cache and then you use it on Profile or elsewhere
https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr

I think you can do something similar with React 15 and cache function
example here
https://youtu.be/N_sUsq_y10U?si=N08_1X0qZuRHKxSv&t=409
View full answer

3 Replies

Avatar
Polar bearOP
The first problem I noticed with my approach is that user gets refetched every time sidebar gets collapsed and opened again.
Avatar
I made something similar with useQuery, prefetching user on Layout, you save it on cache and then you use it on Profile or elsewhere
https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr

I think you can do something similar with React 15 and cache function
example here
https://youtu.be/N_sUsq_y10U?si=N08_1X0qZuRHKxSv&t=409
Answer
Avatar
Komondor
you might be able to create your own context hook too