React应用出现重复Header和Footer问题排查请求
开发的简单React(Remix)应用出现Header和Footer重复渲染问题,页面DOM结构中出现嵌套的<html>与<body>标签,导致Header和Footer各显示两次。
渲染后的DOM结构
<html lang="en"> <head></head> <body> <header>/* header content*/</header> <html lang="en"> <head></head> <body> <header>/* header content*/</header> <h1>Welcome to the main page</h1> <footer>/* footer content*/</footer> </body> </html> <footer>/* footer content*/</footer> </body> </html>
相关代码文件
app/root.tsx
import { Links, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; import Header from "./routes/components/Header"; import Footer from "./routes/components/Footer"; import "./tailwind.css"; import "./style.css"; export function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta /> <Links /> </head> <body> <Header /> {children} <Footer /> <ScrollRestoration /> <Scripts /> </body> </html> ); } export default function App() { return ( <Layout> <Outlet /> </Layout> ); }
app/routes/_index.tsx
import React from 'react'; const Index = () => { return ( <div className='p-4'> <h1>Welcome to the main page</h1> </div> ); }; export default Index;
app/routes/components/Header.tsx
import React from 'react'; const Header: React.FC = () => { return ( <header> <div className="p-4"> <h1 className="text-3xl">My magic app</h1> </div> </header> ); }; export default Header;
app/routes/components/Footer.tsx
import React from 'react'; const Footer: React.FC = () => { return ( <footer> <p className="py-1 text-base sm:text-sm">© 2024, MadFox. All rights reserved.</p> </footer> ); }; export default Footer;
测试验证现象
- 删除
app/root.tsx中Layout函数内的<Header />,页面仅显示两个Footer,无Header内容; - 删除
Layout函数内的<Footer />,页面仅显示两个Header,无Footer内容; - 同时删除
<Header />和<Footer />,页面无内容,内部<body>为空。
问题原因及修复方案
核心原因
Remix框架的约定是:如果在root.tsx中导出了Layout组件,它会自动作为根布局包裹默认导出的App组件的内容。但当前代码中,默认导出的App组件手动将<Outlet />包裹在了<Layout>中,导致Layout被双重渲染:
- Remix自动用
Layout包裹App组件的返回内容; App组件内部又再次渲染了Layout,进而嵌套出完整的<html>/<body>结构。
这就造成了外层<html>的<body>里先渲染Header,然后内层嵌套的<html>里又渲染了Header、页面内容、Footer,最后外层<body>再渲染Footer,最终出现嵌套DOM结构和重复的Header/Footer。
修复方案
修改app/root.tsx的默认导出App组件,直接返回<Outlet />即可,不需要手动包裹<Layout>:
export default function App() { return <Outlet />; }
这样Remix会自动使用导出的Layout组件来包裹App的内容,避免双重渲染,最终得到正常的DOM结构:外层单一的<html>/<body>,包含一次Header、页面内容、Footer。
内容的提问来源于stack exchange,提问作者Anna Andreevna
相关产品推荐
相关产品推荐

