Next.js: Nested dynamic components produce a react warning

Created on 1 Oct 2018  路  32Comments  路  Source: vercel/next.js

Bug report

Describe the bug

I have a dynamic component loading another dynamic component. Both chunks get correctly loaded by react-lodable _before_ main.js is loaded (so chunk reporting is working as expected). However, the inner component generates a warning:

 Warning: Expected server HTML to contain a matching <p> in <div>.

The content doesn't flash (I assume because the component has already been requested, so the "loading" state only lasts a few milliseconds), but the warning still indicates that something went wrong.

To Reproduce

I updated my proof of concept:
https://github.com/arthens/nextjs-canary

Install deps, run it, open http://localhost:3000, you should see a warning in the console.

Expected behavior

There shouldn't be any warning. The chunk has been pre-loaded, so it should always display the correct value and never go into loading state.

System information

  • Version of Next.js: 7.0.1

Additional context

I tried reproducing the problem using react-loadable directly, but I couldn't (note: my react-lodable setup is significantly different because it's part of a large application, so it is possible that this is a react-loadable bug that doesn't get triggered in my app)

bug

Most helpful comment

@timneutkens @zeit/docs on the master branch is producing a similar warning, if you're looking for something to troubleshoot with.

Warning: Expected server HTML to contain a matching <div> in <div>.

screen shot 2018-12-27 at 8 15 51 pm

All 32 comments

The same happens to me. Your reproducible is very simple and it doesn't show that, but in more complex apps the layout of the page gets broken by this tag mismatch.

In production mode the warning is not shown, but the visual break still happens.

@zvictor please provide a reproduction so that I can look into it.

@timneutkens could you reproduce the warning with my proof of concept or do you need me to provide something else?

@timneutkens I just realised my previous proof of concept was wrong (because I forgot to push one commit 馃槥 ), I just pushed a new version. Apologies for the confusion.

without having ran the example yet, it looks like you are importing a page inside a page using Dynamic imports, in general it鈥檚 a bad idea to import pages in pages as ever js file inside the pages directory is a webpack entrypoint

Sorry, that wasn't intentional. I just wanted to import a dynamic component from another dynamic component and didn't pay attention to the fact that I was importing a page. I updated it so that there's only 1 page and 2 components and I'm still getting the warning.

@timneutkens I tried creating a reproducible that would have the visual break, but I failed to figure out what is causing that in my app.

All I managed to do in the end was a reproducible of the warning message, similarly to the reproducible supplied by @arthens

https://github.com/zvictor/nextjs-issue-5347

While taking care of this, it became very clear that as soon as a dynamically imported component imports another component dynamically, the warning message starts appearing.

I confirm the bug since 7.0.1
I'm currently stucked with 7.0.0 because otherwise this error breaks my layout. Since I'm using Drupal for a decoupled CMS with my app, a lot of my components are nested and using next/dynamic. Hope this get fixed soon.

@quietshu let us know if there is anything else we can do to help you on this. Like @mario-iliev stated, we are very interested on finding a solution for this issue.

Is it important for you that we manage to find a reproducible that shows the "visual break" or should we focus on investigating something else?

I'm running into this bug as well. However in my case it appears to be how I'm setting up the components to import. I've also tried reverting to 7.0.0 as @zvictor mentioned and I still see the same error on 7.0.0 so I'm not sure whether this is the same bug or not. I'm not nesting dynamic components within one another, I'm just importing them differently. But the behavior is identical.

When I define an object of components to import such as:

const Components = {};
Components['component-one'] = dynamic(import('./dynamic/component-one'));
Components['component-two'] = dynamic(import( './dynamic/component-two'));

This appears to work fine. But if I build the components dynamically like:

const componentList = [
    'component-one',
    'component-two',
];

const Components = {};
componentList.forEach(path => 
    Components[path] = dynamic(import(`./dynamic/${path}`))
);

Then I end up with the same error on rehydrate in the client:
screen shot 2018-11-13 at 10 36 59 pm

The working repository is here: https://github.com/KB1RMA/next-dynamic-working
Non-working repository is here: https://github.com/KB1RMA/next-dynamic-not-working

Same steps:

Other than this initial reload of the component in the client everything else appears to work fine.

@KB1RMA as much as I know your problem is with the import function it self. You can't use it with variables because it's executing before everything else and what actually is trying to import is ./dynamic/${path} and not ./dynamic/component-one. I'll be glad if I'm wrong, because I have the same problem.

I updated my reproducible trying to include the lessons brought by @KB1RMA.
You can now switch from different modes of how to import the components by uncommenting lines

I have noticed that around 50% of the times I reload my application it will render properly while in the remaining times the main content will jump to the left.

@timneutkens @zeit/docs on the master branch is producing a similar warning, if you're looking for something to troubleshoot with.

Warning: Expected server HTML to contain a matching <div> in <div>.

screen shot 2018-12-27 at 8 15 51 pm

I finally got it! The jumping on the screen happens when static dynamic import is combined with ~Reactive Search~ ThemeProvider. I made a branch on the reproducible repo showing that.

SSR rendering:
image

Client rerendering:
image


What seems to be happening is that ReactiveBase renders all its content properly (_Inner Component, in BLUE_) but somehow messes up with React's internal references. The sibling below a dynamically imported ReactiveBase component will have all its properties/attributes sent to the next component and being replaced by the attributes of the previous component.

SSR rendering:
image

Client rerendering:
image


I am in doubt if this is a bug with dynamic import or with Reactive Search. Uncommenting the line that makes the import run from inside a function makes everything work fine again, even though it is still a dynamic import.

@metagrover would you like me to open an issue for that in Reactive Search as well?

Hello @zvictor, we wrap the content of ReactiveBase component with emotion ThemeProvider. Could that be causing this? 馃

You are right! I created yet another branch that uses emotion's ThemeProvider and the problems exists there as well. I tried both emotion 9 (9.2.9) and 10 (10.0.6).

I am in doubt if this is a bug with dynamic import or with ~Reactive Search~ emotion. Uncommenting the line that makes the import run from inside a function makes everything work fine again, even though it is still a dynamic import.

@mitchellhamilton would you like me to open an issue for that in emotion as well?

Any update on the original issue? Getting the exact same issue and I have the latest version

@zvictor In your example, by using the "Component" on line 8 (inside the export default), you are disabling the SSR and because of that there is no problem. Yes it's still a dynamic import but it's loaded on the client side.
I'm really annoyed by this bug!
Because of which we have to release our application without SSR (which is a very big loss and a problem for us). But in the end, we prefer to be ignored by search engines and have a flickerish user experience then having all of the website chunks to be loaded from the client on first request.

Our app is combo of Next, Express, React and Styled Components.
I really hope this issue will be resolved soon from someone.

Added Connor on this issue.

I'm really annoyed by this bug!

You could contribute to Next.js in many ways, including solving this issue. 馃檪

@timneutkens I tried everything in the past 4 months, but it seems that my knowledge as a developer is not enough to help Next.js community. If I a was able to fix and share the fix, believe me I would :)

No worries, we'll look into it 馃憤

Seems like what's happening is:

  • loadable:initialized = false
  • index.js => outer.js is initialized with loadable:createLoadableComponent() (link)
  • loadable:preloadReady() (link) is called including outer.js and inner.js from __NEXT_DATA__
  • loadable:initialized = true
  • outer.js => inner.js tries initialize with loadable:createLoadableComponent() (link), but loadable:initialized == true, so it can't.

The loading element is hydrated on the client for inner.js only. outer.js get hydrated with the correct component.

console.log() output:

// createLoadableComponent()
loadable.js:111 ["../components/outer"] "initialized:" false

// preloadReady()
loadable.js:264 茠 () {
          return init();
        }
loadable.js:265 ./components/outer.js
loadable.js:264 undefined // this is a value set by createLoadableComponent()
loadable.js:265 ./components/inner.js // because the above is undefined, it cannot hydrate inner.js

// createLoadableComponent()
loadable.js:111 ["../components/inner"] "initialized:" true // somehow this needs to happen before preloadReady()

I've solved the issue in #6326

It's out on next@canary right now, will do a new stable release soon 馃檹 Feel free to verify against canary

Appreciate the fix, Tim! Looking at your changes it's quite obvious now what was going on!

@timneutkens Thank you so much for your fast response to this issue.
Unfortunately the fix didn't resolve the problem in our app. I'll give my best to create a reproducible repository but it's gonna be hard and it'll take some time.
Meanwhile the only thing I can do is to describe several things from our code.

  1. We have multiple levels of nested dynamic components (sometimes up to 10).
  2. They are loaded/constructed recursively.
    Which means that we can have a structure like that:
    A
    ---B
    ---C
    ------A
    ---D

It would help to have a reproduction, otherwise it's going to be hard to investigate.

@timneutkens I was able to reproduce the error, here is the repo with the minimal config, I added the steps to reproduce it.

(https://github.com/jcampalo/next-styled-theme)

@timneutkens this isn't solved yet.

Wrapping next/dynamic in a component show the error.
The following reproduces the error with:
next: 9.1.6

// components/DynamicImporter.js
import dynamic from 'next/dynamic'
import React from 'react'

const DynamicImporter = ({ loader, ssr = true }) => {
  return dynamic({
    loader,
    ssr,
  })
}

export default DynamicImporter
//components/Nav.js
import React from 'react'

function Nav() {
  return 'nav'
}



md5-e51159b7d7967a69cecf2bfe9f5df985



//pages/index.js
import React from 'react'

const Nav = DynamicImporter({ loader: () => import('../components/Nav') })

function Home() {
  return <Nav />
}

You should use next/dynamic directly as documented:

const MyComponent = dynamic(() => import('../components/my-component'))

Main reason for this is that we have to compile a bunch of metadata in order to be able to correctly load the file server-side.

@timneutkens , thanks. It seemed to disable the problem for now

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wagerfield picture wagerfield  路  3Comments

jesselee34 picture jesselee34  路  3Comments

formula349 picture formula349  路  3Comments

irrigator picture irrigator  路  3Comments

rauchg picture rauchg  路  3Comments