我在 NextJS 应用程序中有一个侧边栏,并且有一个主要组件放置在一行中。侧边栏有多个项目,单击每个项目会通过推送路线(更新 url)来更新主要内容
<Preview/>
,但这会重新渲染侧边栏和主要内容。我的理解是组件放置不正确导致了问题。现在我已将侧边栏和主要组件放置在 <Home/>
内
文件夹结构和组件结构应该如何?考虑到 Main 组件是一个动态组件(根据传递给它的 id 进行渲染)。
这是文件夹结构
project-root /
├──src
│ ├── app /
│ │ ├── models /
│ │ │ ├── Log.ts
│ │ ├── services /
│ │ │ ├── LogService.ts
│ │ ├── components /
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Navbar.tsx
│ │ │ ├── Home.tsx
│ │ │ ├── Preview.tsx
│ │ │ └── MainContent.js
│ │ │ │
│ │ ├── logs /
│ │ │ ├──[id]
│ │ │ │ ├── page.tsx
│ │ └── layout.tsx
│ │ └── global.css
│ │ └── page.tsx
这是动态页面
logs/[id]/page.tsx
// logs/[id]/page.tsx
import Home from '@/app/_components/Home';
import LogService from '../../_services/logService';
// This is required for dynamic routing in runtime
export const dynamicParams = true;
export async function generateStaticParams() {
const logService = new LogService();
const logs = await logService.fetchLogs();
return logs.map(log => ({ id: log.id }));
}
export default function Page({ params }: { params: { id: string } }) {
const { id } = params;
return <Home id={id} />
};
// _components/Home.tsx
"use client";
import ViewSidebarRoundedIcon from '@mui/icons-material/ViewSidebarRounded';
import { useTheme } from 'next-themes';
import { useEffect, useState } from 'react';
import Log from '../_models/Log';
import LogService from '../_services/logService';
import IconButton from "./IconButton";
import PSNavbar from "./PSNavbar";
import Pastelog from "./Pastelog";
import Preview from './Preview';
import Sidebar from './Sidebar';
import { Theme } from './ThemeSwitcher';
export default function Home({ id }: { id: string | null }) {
const [showSideBar, setShowSideBar] = useState<boolean>(true);
const [logs, setLogs] = useState<Log[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [selectedLogId, setSelectedLogId] = useState<string | null>(id);
async function fetchLogs() {
setLoading(true);
const logService = new LogService();
const logs = await logService.fetchLogs();
setLogs(logs);
setLoading(false);
}
const checkWindowSize = () => {
if (typeof window !== 'undefined') {
if (showSideBar && window.innerWidth <= 768) {
setShowSideBar(false);
}
}
};
const { theme, setTheme } = useTheme();
useEffect(() => {
fetchLogs();
checkWindowSize();
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
if (mediaQuery.matches) {
setTheme(Theme.DARK);
} else {
setTheme(Theme.LIGHT);
}
window.addEventListener('resize', checkWindowSize);
return () => window.removeEventListener('resize', checkWindowSize);
}, []);
return (
<div className={`flex h-screen ${theme == Theme.DARK ? 'darkTheme' : 'lightTheme'}`}>
{
(showSideBar && <IconButton
className={`fixed top-2 left-2 z-30 ${showSideBar ? '' : 'ml-0'}`}
onClick={() => setShowSideBar(!showSideBar)}
ariaLabel="Close Sidebar"
tooltipPlacement='bottom-start'
>
<ViewSidebarRoundedIcon />
</IconButton>
)
}
<div
className={`fixed top-0 left-0 bottom-0 bg-surface overflow-y-auto transition-width duration-1000 ${showSideBar ? 'w-64' : 'w-0'
}`}>
{showSideBar && (
<Sidebar
loading={loading}
logs={logs}
id={selectedLogId}
onLogClick={(id) => {
if (id) {
router.push
setSelectedLogId(id!);
} else {
setSelectedLogId(null);
}
}}
/>
)}
</div>
{/* Main content */}
<div className={`grow overflow-y-auto transition-all duration-800 ${showSideBar ? 'pl-64' : 'pl-0'}`}>
<div className="flex flex-col h-full">
<PSNavbar
sideBarIcon={!showSideBar}
onToggleSidebar={() => setShowSideBar(!showSideBar)}
/>
{
selectedLogId ? (<Preview
id={selectedLogId}
showNavbar={false}
/>) :
(<Pastelog />)
}
</div>
</div>
</div>
);
}
这种方法的问题是我想在侧边栏中选择列表项时更新 url 它可以工作,但这会触发整个主页的重新渲染(预期),但我希望只渲染主要内容.
此外,如果用户通过链接访问网站
<baseurl>/sidebar-item
,我希望应用程序能够加载选定的侧边栏项目(目前工作正常)
完整源代码在这里
这是上述代码的输出,显示侧边栏重新渲染
您正在使用路由器,它用于重新渲染整个应用程序并重定向到所需的页面。我建议使用组件来简单地切换组件。