-
-
Notifications
You must be signed in to change notification settings - Fork 302
Hydration error occurred in @module-federation/nextjs-mf #3346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
_document implementation is outdated.
|
Even if you do that, the hydration error doesn't go away. My question hasn't been resolved, so why are you closing it? The biggest cause I asked about was hydration error. Have you confirmed that this has been resolved? |
It seems to me that the revalidate function is not performing its function properly. Even after calling the revalidate function, a hydration error continues to occur. When I restart the shell, the hydration error disappears. |
Show me your document code now |
Next.js is currently in maintenance mode. Vercel should be the one you contact |
I'm facing this issue too... FWIW, my document code is using the same methodology as pasted above. I'm also using a custom express server, and have implement a clearRoutes function as documented in Step 1 of https://module-federation.io/practice/frameworks/next/express.html. Step 2 is using your updated code @ScriptedAlchemy as pasted above Here's the full document code /* eslint-disable react/no-danger */
/* eslint-disable no-underscore-dangle */
import * as React from 'react';
import BaseDocument, {
Html, Head, Main, NextScript, DocumentContext, DocumentProps, DocumentInitialProps,
} from 'next/document';
import { CookiesProvider } from 'react-cookie';
import { Response, Request } from 'express';
import Cookies from 'universal-cookie';
import {
revalidate,
FlushedChunks,
flushChunks,
} from '@module-federation/nextjs-mf/utils';
import { addCookieChangeListener } from './server/lib/cookies';
import { AppState } from './contexts';
import { ERROR_500_PATHNAME } from './constants/routes';
import { BaseDocumentProps } from './types/document';
import logger from './logger';
type CustomDocumentProps = BaseDocumentProps & {
initialAppState?: AppState;
chunks: Awaited<ReturnType<typeof flushChunks>>;
};
interface FullCustomDocumentProps extends DocumentProps, CustomDocumentProps { }
type GetInitialPropsReturnType = DocumentInitialProps & CustomDocumentProps;
export default class Document extends BaseDocument<FullCustomDocumentProps> {
static async getInitialProps(context: DocumentContext): Promise<GetInitialPropsReturnType> {
const originalRenderPage = context.renderPage;
const { pathname, req, res } = context;
const request = req as Request;
const response = res as Response;
const cookiesInstance = new Cookies(response.locals.cookies || request.cookies);
addCookieChangeListener(cookiesInstance, response);
context.renderPage = () => originalRenderPage({
// Use https://nextjs.org/docs/advanced-features/custom-document#customizing-renderpage to provide server cookies
// This allows use to NOT pass up cookies in the HTML
enhanceApp: (App) => (props) => (
<CookiesProvider cookies={cookiesInstance}>
<App {...props} />
</CookiesProvider>
),
});
if (pathname && !pathname?.endsWith('_error')) {
console.log('about to revalidate')
await revalidate().then((shouldReload) => {
console.log('shouldReload', shouldReload);
if (shouldReload) {
logger.info('HMR activated, chunks revalidated. Clearing express routes...', { shouldReload });
global.clearRoutes();
}
});
}
const initialProps = await BaseDocument.getInitialProps(context);
const chunks = await flushChunks();
// next.js error page will be compiled at build time, and getInitialProps will be invoked
if (pathname === ERROR_500_PATHNAME) {
return {
...initialProps,
chunks,
};
}
// getInitialProps is executed first in App
const { initialAppState } = response.locals || {};
return {
...initialProps,
initialAppState,
chunks,
};
}
render(): JSX.Element {
const { initialAppState, priorityScripts, chunks } = this.props;
const configMarkup = `window.__CONFIGS__=${JSON.stringify(global.__CLIENT_CONFIGS__)}`;
const initialAppStateMarkup = `window.__INITIAL_APP_STATE__=${JSON.stringify(initialAppState)};`;
return (
<Html>
<Head>
{priorityScripts?.map((scriptData) => <script key={scriptData.id} {...scriptData} />)}
<script
id="configEl"
dangerouslySetInnerHTML={{ __html: configMarkup }}
/>
<script
id="initialAppStateEl"
dangerouslySetInnerHTML={{ __html: initialAppStateMarkup }}
/>
<FlushedChunks chunks={chunks} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
} Here's the express snippet code (currently on Express v4): global.clearRoutes = () => {
(expressApp._router as IRouter).stack = (expressApp._router as IRouter).stack
?.filter?.((k) => !k?.route?.path);
}; to add to this - when I make an update in the producer, and refresh the host page, revalidation does kick off, and about to revalidate
remote_producer hash is different - must hot reload server
shouldReload true
HMR activated, chunks revalidated. Clearing express routes... { shouldReload: true } Currently using Nextjs 13.5.8, and I'm using React.lazy to lazily load the remote component from the producer |
Pull request welcomed. |
Thanks @ScriptedAlchemy , I'll do my best. Contributing to this ecosystem is new to me, so I'm unsure where to start - could you provide any pointers here please, as how you'd approach solving this yourself? That would help a lot! 🙏🏽 Thank you, I'll start looking into this in the meantime... |
Start with the revalidation and hot-reload files. See what the use / do / track. Then search for those things in the next mf plugin or your build source code and check that they exist / are implemented. Hot reload and revalidation are a good starting point. If a double reload fixes the hydration error then that means something is not cleared from require cache in time or correctly. |
Thanks @ScriptedAlchemy, that helps a lot! Will link the PR once I get somewhere 🙌🏽 I'll most likely contribute under my @aaalrightalrightalright personal account then |
Try testing without express but like a hello world app of next. It might be express cache or next hot reload not release. So best to try it without the variable of custom express server |
Thanks for that pointer @ScriptedAlchemy , makes sense and will do! |
Confirmed expressjs is not a factor here via simple producer + consumer create nextjs apps producer: https://github.com/rr-victor-nazareth/debug-nextjs-mf-producer Confirmed that the hydration error continuously occurs with any number of page reloads... With that factored out, will continue investigating the plugin implementations per your notes. |
Hi team, have we fixed it? I am still getting the hydration error. Thank you. |
Describe the bug
If you statically import remoteApp's page component into the shell and deploy it, then change the markup of remoteApp and redeploy it again, a hydration error will occur in the shell.
I 'revalidate' _document.tsx as you suggested, but this doesn't even work. So I waited for 10 to 15 minutes to see if it would update over time, but the hydration error still occurred.
I don't understand. If this is the case, why use module-federation?
Should I use dynamic import (ssr: false) or lazy-load? If that's the case, why do you use next.js?
Reproduction
https://github.com/dfd1123/nextjs-with-turborepo
Used Package Manager
yarn
System Info
Validations
The text was updated successfully, but these errors were encountered: