0

Here is a brief description of the structure of the problem. I have a main navbar (Navbar), some links (NavLinks), and a side panel (this is rendered inside Navbar to make things easier).

// Navbar.tsx    
const Navbar = () => {
      const userID = "some-userID";
      const [ActivePanel, setActivePanel] = useState<React.ComponentType | null>(
        null
      );
    
      return (
        <>
          <header className="sticky top-0 flex items-center justify-between p-10 ">
            <p>Logo Component</p>
    
            <NavLinks setActivePanel={setActivePanel} />
          </header>
          {ActivePanel && (
            <div className="absolute top-0 left-0 w-full h-screen bg-amber-100/50 backdrop-blur-[2px]">
              <div className="fixed top-0 right-0 min-w-[350px] min-h-screen flex flex-col gap-2 p-5 bg-amber-200 shadow-sm">
                <button
                  onClick={() => setActivePanel(null)}
                  className="mb-2 text-sm text-gray-500"
                >
                  close
                </button>
                {ActivePanel}
              </div>
            </div>
          )}
        </>
      );
    };

// NavLinks.tsx
    interface Props {
        setActivePanel: React.Dispatch<React.SetStateAction<React.ComponentType| null>>;
    }
const NavLinks = ({ setActivePanel }: Props) => {
    return (
            <div className="flex items-center gap-5">
              {NAV_ITEMS.map((item) => (
                <button
                  key={item.text}
                  onClick={() => setActivePanel(item.panelComponent)}
                  className="cursor-pointer flex items-center gap-2 p-2"
                >
                  {item.text}
                </button>
              ))}
            </div>
          );
        };

// NAV_ITEMS.ts
    import PanelOne from "@/components/sidePanels/PanelOne";
    import PanelTwo from "@/components/sidePanels/PanelTwo";
    import PanelThree from "@/components/sidePanels/PanelThree";
        export const NAV_ITEMS = [
          {
            text: "Panel One",
            panelComponent: PanelOne,
          },
          {
            text: "Panel Two",
            panelComponent: PanelTwo,
          },
          {
            text: "Panel Three",
            panelComponent: PanelThree,
          }
        ];

This works fine, even though if you hover over ActivePanel, it will give you this problem:

Type 'ComponentType<{}>' is not assignable to type 'ReactNode'. 

Problem: Even though this works, I am actually trying to pass userID, which will ultimately come from useSession from 'next-auth/react', as a prop to the active panel.

Is there a way to render the panel dynamically while passing a prop.

Thank you

Edit:

I made changes to a lot of things.

  1. I changed useState in Navbar to
const [ActivePanel, setActivePanel] = useState<React.ComponentType<{userID: string;}> | null>(null);
  1. I changed Interface and onClick in NavLinks to
interface Props {
  setActivePanel: React.Dispatch<React.SetStateAction<React.ComponentType
<{ userID: string}> | null>>;
}

onClick={() => {
    setActivePanel(() => item.panelComponent)
}

Now it is working fine.

1 Answer 1

0

The issue is you have used {ActivePanel} directly in your JSX. This means you are treating the component as a React node rather than invoking it with props. That is the reason for TypeScript warning and also the reason you can not pass userID to your panels.

Instead of rendering {ActivePanel}, invoke it as a component which allows to pass the userID as a prop.

// Navbar.tsx
const Navbar = () => {
  const userID = "some-userID";
  const [ActivePanel, setActivePanel] = useState<React.ComponentType<any> | null>(
    null
  );

  return (
    <>
      <header className="sticky top-0 flex items-center justify-between p-10 ">
        <p>Logo Component</p>

        <NavLinks setActivePanel={setActivePanel} />
      </header>
      {ActivePanel && (
        <div className="absolute top-0 left-0 w-full h-screen bg-amber-100/50 backdrop-blur-[2px]">
          <div className="fixed top-0 right-0 min-w-[350px] min-h-screen flex flex-col gap-2 p-5 bg-amber-200 shadow-sm">
            <button
              onClick={() => setActivePanel(null)}
              className="mb-2 text-sm text-gray-500"
            >
              close
            </button>
            <ActivePanel userID={userID} />
          </div>
        </div>
      )}
    </>
  );
};
1
  • That actually gave me another error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: <div />. Did you accidentally export a JSX literal instead of a component? I'll post the changes that I made as edit to the question. Thank you for the response Commented Apr 10 at 5:21

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.