英文:
Troubleshooting React issue: warning 'Expected server HTML to contain a matching <header> in <div>' with server-side rendering and Express
问题
I've been trying to set up SSR with react for my personal project and have been running into some issues. The console is throwing a bunch of errors, the first one of which is the warning from the title, and then it defaults to CSR. I am not using NextJS, just express, ejs, node, and react. My setup is as follows:
My server.jsx file, which handles the GET request, renders the component to html, inserts it into my html with ejs, and sends it to the client:
app.get("/campus/:id/locations", async (req, res) => {
const reactComponent = renderToString(<SchoolPage />);
const filePath = path.join(__dirname, "dist", "school-page.ejs");
ejs.renderFile(filePath, { reactComponent }, (err, html) => {
if (err) {
console.error("Error rendering template:", err);
return res.status(500).end();
}
res.send(html);
});
});
My component itself:
export default function SchoolPage() {
return (
<>
<header>
<picture title="Campus Eats">
<source
media="(min-width: 400px)"
srcSet="/images/campus-eats-logo-black.svg"
/>
<img src="/images/campus-eats-logo-mini.svg" alt="campus-eats-logo" />
</picture>
<nav className="places-at">
<h2>Places</h2>
<h2>at</h2>
<div className="search-container">
<MiniSearchBar></MiniSearchBar>
</div>
</nav>
<nav className="login-signup">
<button className="login">Log in</button>
<button className="signup">Sign up</button>
</nav>
</header>
<section>
<ContentContainer></ContentContainer>
</section>
</>
);
}
The ejs file it is injected into:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/school-page.css" />
<script src="/school-page.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Document</title>
</head>
<body>
<div class="root">
<!-- Rendered React component will be injected here -->
<%= reactComponent %>
</div>
</body>
</html>
I have heard this sort of error comes from malformed HTML, but there seems to be little documentation on what sort of structure could cause this. I have heard issues with tables and nesting elements inside <p>
, but nothing about headings. I have run the html generated by the server through several html validators and nothing seems to be wrong with it. I've even tried deleting portions of the component, like the header, but this will just yield a similar error with the next nested component ( Expected server HTML to contain a matching <section>
in <div>
). For reference, here's the html that the server is sending to the frontend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/school-page.css" />
<script src="/school-page.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Document</title>
</head>
<body>
<div class="root">
<!-- Rendered React component will be injected here -->
<header>
<picture title="Campus Eats">
<source media="(min-width: 400px)" srcSet="/images/campus-eats-logo-black.svg"/>
<img src="/images/campus-eats-logo-mini.svg" alt="campus-eats-logo"/>
</picture>
<nav class="places-at">
<h2>Places</h2>
<h2>at</h2>
<div class="search-container">
<input type="search" placeholder="Find my school!" value=""/>
<div class="suggestions">
<ul></ul>
</div>
</div>
</nav>
<nav class="login-signup"><button class="login">Log in</button><button class="signup">Sign up</button></nav>
</header>
<section><div class="locations"></div></section>
</div>
</body>
</html>
I've also heard that if the frontend does some operation to alter the page that it could result in a
英文:
I've been trying to set up SSR with react for my personal project and have been running into some issues. The console is throwing a bunch of errors, the first one of which is the warning from the title, and then it defaults to CSR. I am not using NextJS, just express, ejs, node, and react. My setup is as follows:
My server.jsx file, which handles the GET request, renders the component to html, inserts it into my html with ejs, and sends it to the client:
app.get("/campus/:id/locations", async (req, res) => {
const reactComponent = renderToString(<SchoolPage />);
const filePath = path.join(__dirname, "dist", "school-page.ejs");
ejs.renderFile(filePath, { reactComponent }, (err, html) => {
if (err) {
console.error("Error rendering template:", err);
return res.status(500).end();
}
res.send(html);
});
});
My component itself:
export default function SchoolPage() {
return (
<>
<header>
<picture title="Campus Eats">
<source
media="(min-width: 400px)"
srcSet="/images/campus-eats-logo-black.svg"
/>
<img src="/images/campus-eats-logo-mini.svg" alt="campus-eats-logo" />
</picture>
<nav className="places-at">
<h2>Places</h2>
<h2>at</h2>
<div className="search-container">
<MiniSearchBar></MiniSearchBar>
</div>
</nav>
<nav className="login-signup">
<button className="login">Log in</button>
<button className="signup">Sign up</button>
</nav>
</header>
<section>
<ContentContainer></ContentContainer>
</section>
</>
);
}
The ejs file it is injected into:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/school-page.css" />
<script src="/school-page.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Document</title>
</head>
<body>
<div class="root">
<!-- Rendered React component will be injected here -->
<%- reactComponent %>
</div>
</body>
</html>
I have heard this sort of error comes from malformed HTML, but there seems to be little documentation on what sort of structure could cause this. I have heard issues with tables and nesting elements inside <p>, but nothing about headings. I have run the html generated by the server through several html validators and nothing seems to be wrong with it. I've even tried deleting portions of the component, like the header, but this will just yield a similar error with the next nested component ( Expected server HTML to contain a matching <section> in <div>). For reference, here's the html that the server is sending to the frontend:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="/styles/reset.css" />
<link rel="stylesheet" href="/styles/school-page.css" />
<script src="/school-page.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Document</title>
</head>
<body>
<div class="root">
<!-- Rendered React component will be injected here -->
<header>
<picture title="Campus Eats">
<source media="(min-width: 400px)" srcSet="/images/campus-eats-logo-black.svg"/>
<img src="/images/campus-eats-logo-mini.svg" alt="campus-eats-logo"/>
</picture>
<nav class="places-at">
<h2>Places</h2>
<h2>at</h2>
<div class="search-container">
<input type="search" placeholder="Find my school!" value=""/>
<div class="suggestions">
<ul></ul>
</div>
</div>
</nav>
<nav class="login-signup"><button class="login">Log in</button><button class="signup">Sign up</button></nav>
</header>
<section>
<div class="locations"></div>
</section>
</div>
</body>
</html>
I've also heard that if the frontend does some operation to alter the page that it could result in a mismatch. My search bar component has a suggestions feature which fetches data and displays it, however I can't see this being the issue because when it initially loads it should be the same. Additionally, deleting the component entirely doesn't solve the problem either.
答案1
得分: 2
我怀疑有人会看到这个,但如果有其他人遇到这个问题,我做了两件事来解决这个问题。首先,我怀疑这不重要,但我将<div class="root">
改为了<div id="root">
。更重要的是,可能导致这个问题的是React内联注入组件,这意味着如果你尝试像这样编写你的HTML模板:
<div class="root">
<!-- 渲染的React组件将被注入到这里 -->
<%- reactComponent %>
</div>
React会因为不希望组件在根div的新行上被注入而发出警告。所以你必须像这样编写它:
<div class="root"><%- reactComponent %></div>
一个简单的修复方法,但也容易被忽视。
英文:
I doubt anyone will see this, but if anyone else is running into this issue I did two things to solve this problem. For one, I doubt this is important, but I changed <div class="root">
to <div id="root">
. What was more important, and probably causing this issue was that React injects the component inline, meaning that if you try and write your html template like so:
<div class="root">
<!-- Rendered React component will be injected here -->
<%- reactComponent %>
</div>
React will scream at you because it was not expecting the component to be injected on a new line from the root div. So you have to write it like so:
<div class="root"><%- reactComponent %></div>
A simple fix but also an easy one to overlook.
答案2
得分: 0
以下是翻译好的内容:
错误消息“预期服务器 HTML 包含匹配的
以下是一些你可以查看的事项:
仅客户端操作:如果有仅在客户端执行的操作,在组件挂载时修改 DOM 或 React 状态,这可能导致服务器渲染的 HTML 与客户端不匹配。这些操作包括在 useEffect 中获取数据、根据窗口尺寸设置状态等。
水合问题:React 期望渲染的内容在服务器和客户端之间是相同的。React 用于使服务器渲染的页面具有交互性的过程称为水合。警告存在的原因是因为 React 期望标记(HTML)是相同的,以便可以重用(水合)服务器渲染的 HTML,而不是创建新的 DOM 树。服务器和客户端渲染内容之间的任何差异都可能导致此错误。
React 版本:确保在服务器和客户端上使用相同的 React 版本。不同版本可能具有不同的渲染特性。
异步操作:确保所有数据获取在服务器端开始渲染组件之前完成。你的 MiniSearchBar 和 ContentContainer 可能需要在渲染之前获取数据。考虑使用支持服务器端渲染的数据获取库,如 react-query 或 SWR。
状态不匹配:对于具有状态的组件,在服务器和客户端渲染之间,如果初始状态不同,可能会导致此问题。
没有完整的代码,很难准确定位确切的问题。然而,这些步骤应该帮助你调试此问题。我建议首先确保所有组件在服务器端正确渲染所需的数据,并且在组件完全水合之前没有在客户端上运行仅适用于客户端的操作。
对于更复杂的应用程序,你可能希望考虑使用像 Next.js 这样专为服务器端渲染构建的框架,它们抽象了许多常见的陷阱。
英文:
The error message "Expected server HTML to contain a matching <header> in <div>" typically happens when the server-rendered HTML differs from what the client is attempting to render. This can occur when certain parts of your component use client-only data or perform operations that aren't executed on the server.
Here are few things you can look into:
Client-Side Only Operations: If there are client-side only operations that modify the DOM or React state when the component mounts, this can cause a mismatch between the server-rendered HTML and the client. These operations include things like fetching data in useEffect, setting state based on window dimensions, etc.
Hydration Issue: React expects that the rendered content is identical between the server and the client. React's process of making a server-rendered page interactive is called hydration. The warning is there because React expects the markup (HTML) to be identical so it can reuse (hydrate) the server-rendered HTML, instead of creating a new DOM tree. Any difference between server and client rendered content can lead to this error.
React Version: Ensure that you're using the same React version on the server as on the client. Different versions may have different rendering characteristics.
Asynchronous Operations: Make sure that all the data fetching happens before you start rendering your component on the server side. Your MiniSearchBar and ContentContainer may need data before they can render. Consider using a data fetching library that supports server side rendering like react-query or SWR.
State Mismatch: In case of a stateful component, if initial state is different between server and client render, it could cause this issue.
Without the complete code, it's hard to pinpoint the exact problem. However, these steps should help you to debug the issue. I would suggest starting by ensuring all components have the necessary data to correctly render on the server side, and there are no client-only operations running before the component has been fully hydrated.
For more complex apps, you might want to consider frameworks like Next.js that are built for server-side rendering and abstract away a lot of these common pitfalls.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
- express
- hydration
- javascript
- reactjs
- server-side-rendering
评论