You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js中点击链接在布局内渲染组件(保持导航页脚不刷新)

实现Next.js页脚链接切换中间内容(Nav/Footer不重载)

要实现点击页脚链接时仅切换导航和页脚之间的内容,不触发页面跳转,核心是用客户端状态管理替代传统的a标签跳转,具体步骤如下:


1. 给页脚链接添加唯一标识

修改_data.tsx,为每个链接添加用于区分内容的key(替代原有的href):

export const links = [
    {
      title: 'Company',
      links: [
        { label: 'About us', key: 'about' },
        { label: 'Contact', key: 'contact' },
      ],
    },
]

2. 修改Footer组件,添加点击回调

将Footer改为受控组件,移除a标签的href,添加onClick事件并传递链接的key:

import {
    Box,
    Button,
    SimpleGrid,
    Stack,
    Text,
  } from '@chakra-ui/react'
  import * as React from 'react'
  import { Logo } from '../../components/Logo'
  import { links } from './_data'
  
// 定义props类型,接收点击回调
interface FooterProps {
  onLinkClick: (key: string) => void;
}

export const Footer = ({ onLinkClick }: FooterProps) => (
    <Box
      w="100%"
      mt={10}
      borderTop="1px solid"
      borderColor="gray.200"
      zIndex={500}
    >
      <Box w="80%" ml="10%">
                <Stack
                justify="space-between"
                align="start"
                direction={{ base: 'column', lg: 'row' }}
                py={{ base: '12', md: '16' }}
                spacing="8"
                >
                <Stack spacing={{ base: '6', md: '8' }} align="start">
                    <Logo />
                </Stack>
                <SimpleGrid columns={{ base: 3 }} gap="20" width={{ base: 'full', lg: 'auto' }} >
                    {links.map((group, idx) => (
                    <Stack key={idx} spacing="4" minW={{ lg: '40' }} >
                        <Text fontSize="sm" fontWeight="semibold" >
                        {group.title}
                        </Text>
                        <Stack spacing="3" shouldWrapChildren>
                        {group.links.map((link, idx) => (
                            <Button 
                                key={idx} 
                                variant="link" 
                                // 点击时触发回调,传递当前链接的key
                                onClick={() => onLinkClick(link.key)}
                                fontWeight="normal" 
                                color="brand.sludge"
                                _hover={{ color: "green" }}
                            >
                            {link.label}
                            </Button>
                        ))}
                        </Stack>
                    </Stack>
                    ))}
                </SimpleGrid>
                </Stack>
      </Box>
    </Box>
  )

3. 在页面组件中维护状态,切换内容

以首页(app/page.tsx)为例,标记为客户端组件(用'use client'),通过useState管理当前激活的内容,根据状态渲染对应组件:

'use client'; // 必须标记,因为要使用React状态

import { useState } from 'react';
import { HomeLayout } from '@/components/HomeLayout';
// 导入你的内容组件
import { AboutUs } from '@/components/AboutUs';
import { Contact } from '@/components/Contact';
import { HomeContent } from '@/components/HomeContent';

export default function HomePage() {
  // 状态:当前激活的内容key,初始为null显示默认内容
  const [activeContent, setActiveContent] = useState<string | null>(null);

  // 处理页脚链接点击,更新状态
  const handleLinkClick = (key: string) => {
    setActiveContent(key);
    // 可选:点击后滚动到页面顶部
    window.scrollTo({ top: 0, behavior: 'smooth' });
  };

  // 根据状态渲染对应组件
  const renderActiveContent = () => {
    switch(activeContent) {
      case 'about':
        return <AboutUs />;
      case 'contact':
        return <Contact />;
      default:
        return <HomeContent />;
    }
  };

  return (
    <HomeLayout onFooterLinkClick={handleLinkClick}>
      {renderActiveContent()}
    </HomeLayout>
  );
}

4. 更新HomeLayout,传递回调给Footer

修改HomeLayout组件,添加接收点击回调的props,并传递给Footer:

import * as React from "react"
import { Box } from "@chakra-ui/layout"

import { Limiter } from "./Limiter"
import { Nav } from "./Nav"
import { Footer } from "components/footer/index"

interface Props {
  children: React.ReactNode;
  onFooterLinkClick: (key: string) => void;
}

export function HomeLayout({ children, onFooterLinkClick }: Props) {
  return (
    <Box>
      <Nav />
      <Limiter pt="65px">{children}</Limiter>
      {/* 将回调传递给Footer */}
      <Footer onLinkClick={onFooterLinkClick} />
    </Box>
  )
}

替代方案:在HomeLayout内部管理状态

如果不想在页面组件中维护状态,也可以将状态直接放在HomeLayout中(需标记为客户端组件):

'use client';

import * as React from "react"
import { Box } from "@chakra-ui/layout"
import { useState } from 'react';

import { Limiter } from "./Limiter"
import { Nav } from "./Nav"
import { Footer } from "components/footer/index"
import { AboutUs } from '../AboutUs';
import { Contact } from '../Contact';
import { HomeContent } from '../HomeContent';

export function HomeLayout() {
  const [activeContent, setActiveContent] = useState<string | null>(null);

  const renderContent = () => {
    switch(activeContent) {
      case 'about':
        return <AboutUs />;
      case 'contact':
        return <Contact />;
      default:
        return <HomeContent />;
    }
  };

  return (
    <Box>
      <Nav />
      <Limiter pt="65px">{renderContent()}</Limiter>
      <Footer onLinkClick={(key) => setActiveContent(key)} />
    </Box>
  )
}

这种情况下,页面组件只需直接使用<HomeLayout />即可。


核心原理

通过React的状态管理,在客户端层面切换中间区域的组件,避免了浏览器的页面跳转,因此Nav和Footer组件会始终保留在DOM中,不会重新加载。

内容的提问来源于stack exchange,提问作者Mel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 05:55:47