typescript - React router V6: วิธีการมีช่องทางการนำทางสองช่องที่ทำงานพร้อมกันและเป็นอิสระ
ฉันกำลังสร้างเว็บไซต์ที่มี 2 ส่วน (เรียกว่าซ้ายและขวา) และในแต่ละส่วนมีแท็บ
บางอย่างเช่นนี้:
สิ่งที่ฉันต้องการคือ URL แบบนี้:
"https://mywebsite?right=billing&left=stock"โดยที่ 'left=' และ 'right=' ถูกใช้เพื่อหาคอนเทนต์ที่จะนำมาแสดง
โครงสร้างแอปแบบง่าย
ฉันมีคอมโพเนนต์ Split ซึ่งมีคอมโพเนนต์ลูก PanelLeft และ PanelRight:
แอป.tsx
<Split
sizes={60, 40}
direction="horizontal"
>
<PanelLeft />
<PanelRight />
</Split>
const PanelLeft = () => {
const leftPart: string = extractQueryFromUrl('left') // give me 'stock'
return <Outlet context={{ leftPart }} />;
};
const PanelRight= () => {
const rightPart: string = extractQueryFromUrl('right') // give me 'billing'
return <Outlet context={{ rightPart }} />;
};
main.tsx
const router = createBrowserRouter([
{
path: "/",
element: <App/>,
children: [
{
path: "",
element: <PanelLeft />,
children: generateChildRoutes(),
},
{
path: "",
element: <PanelRight />,
children: generateChildRoutes(),
},
],
},
]);
const generateChildRoutes = () => [
{ path: "/", element: <Home /> },
{ path: "stock", element: <Stock /> },
{ path: "billing", element: <Billing /> },
];
createRoot(document.getElementById("root")!).render(
<RouterProvider router={router}>
</RouterProvider>
);
อย่างชัดเจน การแมปใน createBrowserRouter และ <Outlet context={{leftPart}}/> ไม่ทำงานเลย
ฉันสงสัยว่าฉันจะกำหนดค่าพวกมันอย่างไรให้สามารถมีแผงนำทางสองอันที่เป็นอิสระได้
หมายเหตุ: แอปจริงซับซ้อนกว่านี้มาก ใช้ redux เพื่อดึงสถานะตาม tabId ที่ไม่ซ้ำกัน ฯลฯ มันทำงานได้ดีมากกับ Outlet หนึ่งตัว และตัวอย่างเช่น สองเส้นทางย่อย (https://mywebsite/panelright/billing และ https://mywebsite/panelleft/billing) ดังนั้นปัญหาจริง ๆ คือการแสดงผลพร้อมกันอย่างอิสระ
แก้ไข (22/09/2024)
ขอขอบคุณทั้ง @Drew Reese และ @Matthew สำหรับเวลาของคุณและคำอธิบายของคุณ คุณพูดถูกที่การมีหลาย Outlet ไม่มีความหมายและจะไม่มีทางทำงานตามที่ตั้งใจไว้ สุดท้ายฉันได้แนวทางแก้ไขที่ใกล้กับคำแนะนำของ Matthew โดยมี outlet หนึ่งและใช้ตรง ๆ บนอันอื่น
อะไรประมาณนี้
export const tabsRoutesMap: Map<string, JSX.Element> = new Map([
[Billing_Path, <Billing />],
[Stock_Path, <Stock/>],
//... others possible tabs...
]);
const router = createBrowserRouter([
{
path: "/",
element: <App/>,
// generate children from tabs route
children: Array.from(tabsRoutesMap, ([path, element]) => ({ path, element })),
},
]);
แล้วก็ :
<>
{tabsRoutesMap.get(extractedPathFromUrl)}
</>
คำตอบ #1
ปัญหาคือคุณมีเบราว์เซอร์และมีเพียงเส้นทาง URL หนึ่งเท่านั้นที่จะจับคู่และซิงโครไนซ์เส้นทาง ดังนั้นจึงไม่สมเหตุสมผลที่จะมี Outlet มากกว่าหนึ่งตัวในระดับความลึกใด ๆ เพราะมีเพียงเส้นทางเดียวที่สามารถจับคู่และแสดงผลได้
ถ้าคุณต้องการ/จำเป็นต้องมีเราเตอร์/ปลั๊กไฟ ฯลฯ แยกกัน คุณก็มีโอกาสสูงมากที่จะต้องใช้วิธีแก้ปัญหาแบบลงมือทำเอง/ด้วยมือ
นี่คือตัวอย่าง Proof of Concept ที่ใช้สองคอมโพเนนต์ MemoryRouter ที่เป็นพี่น้องกัน หนึ่งอันในแต่ละ "แผง" ซึ่งอ่าน window.location.search ปัจจุบันเพื่อซิงโครไนซ์ตัว router ของแต่ละแผงกับค่าของ แผงนั้น
ตัวอย่าง :
import { useNavigate } from "react-router-dom";
const PanelLayout = ({ panel }) => {
const navigate = useNavigate();
// Get current "global" URL search params
const searchParams = new URLSearchParams(window.location.search);
// Get current panel's search value
const target = searchParams.get(panel) ?? "";
// Synchronize the panel's MemoryRouter to current target panel path
useEffect(() => {
navigate(`/${target}`, { replace: true });
}, [target]);
return (
<>
...
<Outlet />
...
</>
);
};
const generateChildRoutes = (panel) => [
{
element: <PanelLayout panel={panel} />,
children: [
{ path: "/", element: <Home /> },
{ path: "stock", element: <Stock /> },
{ path: "billing", element: <Billing /> },
],
},
];
const leftRouter = useMemo(
() => createMemoryRouter(generateChildRoutes("left")),
[]
);
const rightRouter = useMemo(
() => createMemoryRouter(generateChildRoutes("right")),
[]
);
...
<Split
sizes={ 60, 40 }
direction="horizontal"
>
<PanelLeft>
<RouterProvider router={leftRouter} />
</PanelLeft>
<PanelRight>
<RouterProvider router={rightRouter} />
</PanelRight>
</Split>
หลัก - คุณไม่สามารถซ้อนตัวจัดเส้นทางได้ ดังนั้น BrowserRouter รากจะถูกลบออก
createRoot(document.getElementById("root")!).render(
<App />
);
เดโม
| ซ้าย | ขวา | ตัวอย่าง |
|---|---|---|
| บ้าน | บ้าน | |
| หุ้น | บ้าน | |
| หุ้น | การเรียกเก็บเงิน | |
| บ้าน | การเรียกเก็บเงิน |
คำตอบ #2
ถ้าคุณต้องการสร้างมันด้วย createBrowserRouter คุณสามารถทำได้โดยใช้ outlet เป็นคอมโพเนนต์ด้านขวาและคอมโพเนนต์ด้านซ้ายเป็นปกติ
const Layout = ({ Component }) => (
<div style={{ display: "flex" }}>
<div className="box">
<h2>Left Section</h2>
<Component />
</div>
<div className="box">
<h2>Right Section</h2>
<Outlet context="right" />
</div>
</div>
);
const children = () => {
return [
{ path: "1", element: <Component1 /> },
{ path: "2", element: <Component2 /> },
{ path: "3", element: <Component3 /> },
];
};
const router = createBrowserRouter([
{
path: "/",
element: <RoutesExample />,
children: [
{
path: "1",
element: <Layout Component={Component1} />,
children: children(),
},
{
path: "2",
element: <Layout Component={Component2} />,
children: children(),
},
{
path: "3",
element: <Layout Component={Component3} />,
children: children(),
},
],
},
]);
เดโม
และถ้าคุณต้องการใช้พารามิเตอร์ URL นี่คือวิธีแก้ปัญหาง่ายๆ
const Table = () => {
const [searchParams, setSearchParams] = useSearchParams();
const left = searchParams.get("left");
const right = searchParams.get("right");
ComponentLeft = componentList[left];
ComponentRight = componentList[right];
return left && right ? (
<div style={{ display: "flex" }}>
<div className="box">
<h2>Left Section</h2>
<ComponentLeft />
</div>
<div className="box">
<h2>Right Section</h2>
<ComponentRight />
</div>
</div>
) : (
<div></div>
);
};
ที่ไหน
const Component1 = () => {
return <div>1</div>;
};
const Component2 = () => {
return <div>2</div>;
};
const Component3 = () => {
return <div>3</div>;
};
const componentList = {
1: Component1,
2: Component2,
3: Component3,
};
เดโม




