Skip to content
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

Critical styles missing during server render with Next.js #1079

Closed
dcalhoun opened this issue Aug 20, 2018 · 11 comments
Closed

Critical styles missing during server render with Next.js #1079

dcalhoun opened this issue Aug 20, 2018 · 11 comments

Comments

@dcalhoun
Copy link
Contributor

The problem
When server rendering a Next.js application, the "critical" styles for the given page are missing. They apply correctly, however, in the client.

How to reproduce
Simplified test case: https://github.com/HealthTeacher/nextjs-rnw

Steps to reproduce:

  1. Clone the test case.
  2. yarn install
  3. yarn start
  4. Open http://localhost:3000
  5. Disable JavaScript in your browser.
  6. Refresh the page.

Expected behavior
The "critical" styles for the page are rendered by the server.

Environment (include versions). Did this work in previous versions?

  • React Native for Web (version): 0.8.9
  • React (version): 16.4.2
  • Browser: macOS Safari

Notes:

@steida
Copy link

steida commented Sep 2, 2018

I am using RNW here in Este and it seems it works. Maybe it can help.

https://github.com/este/este

@dcalhoun
Copy link
Contributor Author

dcalhoun commented Sep 2, 2018

@steida thanks for the tip. Unfortunately, you are utilizing the CommonJS version of RNW’s modules, which I am already able to get working.

My primary goal is to have server styles work when I compile RNW’s ES module exports with Babel.

@necolas
Copy link
Owner

necolas commented Sep 3, 2018

Have you looked into why? What's different about the bundle?

@dcalhoun
Copy link
Contributor Author

dcalhoun commented Sep 3, 2018

@necolas I have done a fair amount of debugging, but have been unable to identify what is going wrong.

The only piece I’ve noticed and don’t quite understand is that, on the server, this._sheet is always null here because it is only set on the client. That means any non-initialStyles never gets “inserted” into the style sheet. How/where are styles inserted on the server?

If you don’t have any specific thoughts or guidance, I’ll try to dive deeper and see if I can find something. I understand you can’t debug every integration/application.

Thank you for your hard work on RNW.

@necolas
Copy link
Owner

necolas commented Sep 3, 2018

Yeah the server only needs rules pushed into the array.

My first thought is that webpack is dropping some code. If the issue didn't exist for cjs understanding the difference between the 2 module formats is probably where I'd start

@dcalhoun
Copy link
Contributor Author

dcalhoun commented Sep 6, 2018

@necolas below is a log of a single page load run. I'm not sure what is occurring here.

Context

  • Any > NAME is just a top-level console.log from the file NAME.
  • Any > NAME.METHOD is a console.log from within the method itself (e.g. the constructor method of ReactNativeStyleResolver).
  • The number at the end of WebStyleSheet.constructor is a simple index incremented at the top of the WebStyleSheet file as an attempt to understand how many times the file is imported and the class instantiated.

ES Modules Log

> styleResolver
> ReactNativeStyleResolver.constructor
> ReactNativeStyleResolver.init
> StyleSheetManager.constructor
> WebStyleSheet.constructor 0

> createDOMProps
> NativeMethodsMixin
> styleResolver
> ReactNativeStyleResolver.constructor
> ReactNativeStyleResolver.init
> StyleSheetManager.constructor
> WebStyleSheet.constructor 0

> createDOMProps
> NativeMethodsMixin
> renderApplication
> renderApplication.getApplication
> ReactNativeStyleResolver.init
> StyleSheetManager.constructor
> WebStyleSheet.constructor 1

CJS Modules Log

> styleResolver
> ReactNativeStyleResolver.constructor
> ReactNativeStyleResolver.init
> StyleSheetManager.constructor
> WebStyleSheet.constructor 0

> createDOMProps
> NativeMethodsMixin
> renderApplication
> renderApplication.getApplication
> ReactNativeStyleResolver.init
> StyleSheetManager.constructor
> WebStyleSheet.constructor 1

As you can see, the top-level styleResolver file (and the resulting method chain) logs twice for ES modules, but once for CJS modules. I presume the duplicative execution of styleResolver is the culprit for resetting this._cssRules and emptying the custom styles on the server. createDOMProps and NativeMethodsMixin depend upon styleResolver, but those only show up in the second run.

Questions

  1. Why would > styleResolver show up twice?
  2. What file causes > styleResolver to run the first time?
  3. Why would createDOMProps and NativeMethodsMixin not show up in the second run?

I'll try to keep digging, but wanted to pose these questions in case something stood out to you. My second question is the primary question blocking me from digging deeper.

Thanks again for the help.

@dcalhoun
Copy link
Contributor Author

dcalhoun commented Sep 9, 2018

@necolas below is the dependency tree arriving at styleResolver.js for Next.js' page/index.js (root route) and pages/_document.js (shared wrapping component for each page). During execution,styleResolver.js is imported first by pages/index.js and then later by pages/_document.js. The bolded styleResolver.js below (i.e. iii underneath pages/_document.js) is the focus of the issue.

When using ES modules, a breakpoint in styleResolver.js is hit twice. When using CJS modules, the breakpoint is hit once. With CJS, it seems as though webpack caches the singleton styleResolver and does not import the file a second time.

Do you know why the styleResolver.js singleton executes twice for ES modules, but only once for CJS modules?

  • pages/index.js
    1. Text
    2. TextStylePropTypes.js
    3. applyNativeProps.js
    4. NativeMethodsMixin.js
    5. createDOMProps.js
    6. styleResolver.js
  • pages/_document.js
    1. AppRegistery.js
    2. renderApplication.js
    3. styleResolver.js - CJS never hits a debugger here, ES does.

@necolas
Copy link
Owner

necolas commented Sep 9, 2018

Do you know why the styleResolver.js singleton executes twice for ES modules, but only once for CJS modules?

Nope. Can't tell if this is a webpack or Next.js thing either

@dcalhoun
Copy link
Contributor Author

Can't tell if this is a webpack or Next.js thing either

Alright. I agree this is likely not an issue with RNW. I'll close this issue and open an issue in the Next.js community.

Thank you for guidance.

@necolas
Copy link
Owner

necolas commented Sep 10, 2018

Oh I don't know, it might be something to do with RNW. I don't mind leaving this open until we know what the issue is

@necolas necolas reopened this Sep 10, 2018
@dcalhoun
Copy link
Contributor Author

I was able to resolve this issue by upgrading to next@^7.0.0-canary.15. The explanation of this issue may be found here.

TL;DR – webpack doesn't use require.cache when compiling to the Node.js target, instead it uses it's own installed modules cache, which caused issues for the way Next.js bundled pages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants