NextJS Route.push 从侧边栏重建整个应用程序

问题描述 投票:0回答:1

我在 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
,我希望应用程序能够加载选定的侧边栏项目(目前工作正常)

完整源代码在这里

这是上述代码的输出,显示侧边栏重新渲染

enter image description here

next.js web-applications navigation next-router
1个回答
0
投票

您正在使用路由器,它用于重新渲染整个应用程序并重定向到所需的页面。我建议使用组件来简单地切换组件。

© www.soinside.com 2019 - 2024. All rights reserved.