Next.js Discord

Discord Forum

Parallell routes state lifting

Answered
Bordin posted this in #help-forum
Open in Discord
Hi, I am trying to make a responsive top bar. I need to lift a state from the topbar, which is a parallel route to the layout, and then pass it to the sidebar.

this is layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "../scss/DashboardPage.scss";
import "../scss/TopBar.scss";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
  children,
  CategoryDashboard,
  TopBar,
}: Readonly<{
  children: React.ReactNode;
  CategoryDashboard: React.ReactNode;
  TopBar: React.ReactNode;
}>) {
  return (
    <div style={{ display: "flex" }}>
      <div>{children}</div>
      <div id="RightSideDashboard">
        {TopBar}
        {CategoryDashboard}
      </div>
    </div>
  );
}

I technically cant use state in layout.tsx can i? because its not a client component
Answered by Ray
there is two way to do it
1. use query params as state
2. use react context
View full answer

37 Replies

"use client";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "../scss/DashboardPage.scss";
import "../scss/TopBar.scss";
import { useState } from "react";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
  children,
  CategoryDashboard,
  TopBar,
}: Readonly<{
  children: React.ReactNode;
  CategoryDashboard: React.ReactNode;
  TopBar: React.ReactNode;
}>) {
  const [showDashboard, setShowDashboard] = useState(false);
  return (
    <div style={{ display: "flex" }}>
      <div>{children}</div>
      <div id="RightSideDashboard">
        {TopBar setShowDashboard={setShowDashboard}}
        {CategoryDashboard}
      </div>
    </div>
  );
}

trying to pass the setstate function
doesnt work
@Bordin ray might have a better solution, but maybe you can just do a document.getElementById and hide it that way?
so you dont have to lift state
I dont think that's a good practice
we dont use that in react
yea tbf, ray to the rescue lol
hmm i dont think it is possible. I have to create another react componennt and call rootlayout from there.
i think ill just use localstorage
Answer
you can dispatch custom events and listen for them
@Arinji you can dispatch custom events and listen for them
I don't use custom event in react 👀
i used it for one project (i didnt know how to revalidate cache back then lol)
@Ray there is two way to do it 1. use query params as state 2. use react context
can i use context in layout.tsx?
i was trying localstorage.
  var status = localStorage.getItem("NavBar");
  localStorage.setItem("NavBar", "false");
  //  }, []);
  const elementRef = useRef<HTMLDivElement>(null); // Specify the type of the ref

  useEffect(() => {
    const element = elementRef.current;
    if (element) {
      if (status == "true") {
        element.classList.add("Slidein");
      } else {
        element.classList.remove("Slidein");
      }
    }
  }, [status]);

its just not refreshing
its not up to date u know?
Okay
// provider.tsx
'use client'

const context = createContext({});

export function Provider({ children }: { children: ReactNode }) {
  const [open, setOpen] = useState(false);
  return (
    <context.Provider value={{ open, setOpen }}>
      {children}
    </context.Provider>
  );
}

export function useOpen() {
  return useContext(context)
}

// layout.tsx
import { Provider } from './provider'

export default function RootLayout({
  children,
  CategoryDashboard,
  TopBar,
}: Readonly<{
  children: React.ReactNode;
  CategoryDashboard: React.ReactNode;
  TopBar: React.ReactNode;
}>) {
 return (
<Provider>
  <div style={{ display: "flex" }}>
        <div>{children}</div>
        <div id="RightSideDashboard">
          {TopBar}
          {CategoryDashboard}
        </div>
      </div>
</Provider>
  );
}
then create a client component when you need the state
'use client'
export function Client() {
  const {open, setOpen} = useOpen()
}
[{"resource": "devicemanagement/src/app/Dashboard/layout.tsx",

    "message": "Module '\"./provider\"' has no exported member 'Provider'. Did you mean to use 'import Provider from \"./provider\"' instead?",
}]
"use client";

import { ReactNode, createContext, useState } from "react";

const context = createContext({});

export default function Provider({ children }: { children: ReactNode }) {
  const [open, setOpen] = useState(false);
  return (
    <context.Provider value={{ open, setOpen }}>{children}</context.Provider>
  );
}

//layout
"use client";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "../scss/DashboardPage.scss";
import "../scss/TopBar.scss";
import { useState } from "react";
import { Provider } from "./provider";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
  children,
  CategoryDashboard,
  TopBar,
}: Readonly<{
  children: React.ReactNode;
  CategoryDashboard: React.ReactNode;
  TopBar: React.ReactNode;
}>) {
  const [showDashboard, setShowDashboard] = useState(false);
  return (
    <Provider>
      <div style={{ display: "flex" }}>
        <div>{children}</div>
        <div id="RightSideDashboard">
          {TopBar}
          {CategoryDashboard}
        </div>
      </div>
    </Provider>
  );
}
nvm
fixedit
hmm
also export this custom hook from provider.tsx
// provider.tsx
export function useOpen() {
  return useContext(context)
}
@Bordin hmm
its fine, it doesn't matter
import * as React from "react";
import TopBar from "@/app/components/TopBar";
import { useOpen } from "../provider";
interface OpenState {
  open: boolean;
  setOpen: React.Dispatch<React.SetStateAction<boolean>>;
}
export default function TobBar() {
  const { open, setOpen } = useOpen() as OpenState;

  return (
    <div style={{ color: "black" }} id="TopBarLayout">
      <TopBar setOpen={setOpen} />
    </div>
  );
}
ah mybad
lets see if it works
its working !! but i need to fix the css
@Ray thank you very much!!!