Parallell routes state lifting
Answered
Bordin posted this in #help-forum
BordinOP
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
I technically cant use state in layout.tsx can i? because its not a client component
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
1. use query params as state
2. use react context
37 Replies
BordinOP
"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
BordinOP
I dont think that's a good practice
we dont use that in react
yea tbf, ray to the rescue lol
BordinOP
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
i think ill just use localstorage
Answer
@Ray there is two way to do it
1. use query params as state
2. use react context
maybe the events api as well?
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
BordinOP
can i use context in layout.tsx?
BordinOP
i was trying localstorage.
its just not refreshing
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?
BordinOP
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()
}BordinOP
[{"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
BordinOP
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
BordinOP
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>
);
}or mark this as client component
BordinOP
ah mybad
lets see if it works
BordinOP
its working !! but i need to fix the css
@Ray thank you very much!!!