reactjs / React.NET

.NET library for JSX compilation and server-side rendering of React components
https://reactjs.net/
MIT License
2.29k stars 938 forks source link

EmotionFunctions are broken with emotion 10+ #970

Open mbaumanndev opened 4 years ago

mbaumanndev commented 4 years ago

Please verify these steps before filing an issue, and check them off as you go

I'm using these library versions:

Runtime environment:

Steps to reproduce

Take the sample code from React.Sample.Webpack.CoreMvc and upgrade the project to ASP.NET Core 3.1 preview 3 (I assume that the behaviour is the same with 3.0, but I havn't tried).

Remove the react-emotion package (v10 throw an error explaining that it's deprecated and should be replaced with @emotion/core and @emotion/styled

At the end, I have the following package.json :

{
  "name": "my-app",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "@babel/core": "^7.7.2",
    "@babel/plugin-proposal-class-properties": "^7.7.0",
    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
    "@babel/preset-env": "^7.7.1",
    "@babel/preset-react": "^7.7.0",
    "@babel/preset-typescript": "^7.7.2",
    "babel-loader": "^8.0.6",
    "babel-plugin-emotion": "^10.0.23",
    "babel-runtime": "^6.26.0",
    "react-select": "^3.0.4",
    "reactstrap": "^8.0.0",
    "webpack": "^4.41.2",
    "webpack-cli": "^3.3.10"
  },
  "dependencies": {
    "@emotion/core": "^10.0.22",
    "@emotion/styled": "^10.0.23",
    "emotion": "^10.0.23",
    "emotion-server": "^10.0.17",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-helmet": "^5.2.1",
    "react-jss": "^10.0.0",
    "react-router-dom": "^5.1.2",
    "styled-components": "^4.0.0"
  }
}

In Content/component/emotion.jsx, replace import styled from 'react-emotion with import styled from '@emotion/styled';


When launching the app, an error occurs :

React.Exceptions.ReactScriptLoadException: Error while loading "~/dist/components.js": ReferenceError: 'document' is not defined

 ---> JavaScriptEngineSwitcher.Core.JsRuntimeException: ReferenceError: 'document' is not defined

Engine name: ChakraCoreJsEngine

Engine version: 1.11.13

Category: Runtime error

Description: 'document' is not defined

Type: ReferenceError

Document name: eval code

Line number: 109

Column number: 5

Source fragment:     container = options.container || document.head; ---> JavaScriptEngineSwitcher.ChakraCore.JsRt.JsScriptException: Script threw an exception.

   at JavaScriptEngineSwitcher.ChakraCore.JsRt.JsErrorHelpers.ThrowIfError(JsErrorCode error)

   at JavaScriptEngineSwitcher.ChakraCore.JsRt.JsContext.RunScript(String script, JsSourceContext sourceContext, String sourceUrl, JsParseScriptAttributes& parseAttributes)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.<>c__DisplayClass26_0.<InnerExecute>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.<>c__DisplayClass26_0.<InnerExecute>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.<>c__DisplayClass11_0.<Invoke>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.StartThread()

--- End of stack trace from previous location where exception was thrown ---

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.InnnerInvoke(Func`1 del)

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.Invoke(Action action)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.InnerExecute(String code, String documentName)

   at JavaScriptEngineSwitcher.Core.JsEngineBase.Execute(String code, String documentName)

   at React.JavaScriptEngineFactory.LoadUserScripts(IJsEngine engine)

   --- End of inner exception stack trace ---

   at React.JavaScriptEngineFactory.EnsureValidState()

   at React.JavaScriptEngineFactory.GetEngine()

   at React.ReactEnvironment.<.ctor>b__16_1()

   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)

   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)

   at System.Lazy`1.CreateValue()

   at System.Lazy`1.get_Value()

   at React.ReactEnvironment.get_Engine()

   at React.ReactEnvironment.EnsureUserScriptsLoaded()

   at React.ReactEnvironment.CreateComponent(IReactComponent component, Boolean clientOnly)

   at React.Router.ReactEnvironmentExtensions.CreateRouterComponent[T](IReactEnvironment env, String componentName, T props, String path, String containerId, Boolean clientOnly)

   at React.Router.HtmlHelperExtensions.ReactRouter[T](IHtmlHelper htmlHelper, String componentName, T props, String path, String htmlTag, String containerId, Boolean clientOnly, Boolean serverOnly, String containerClass, Action`2 contextHandler, IRenderFunctions renderFunctions)

   at AspNetCore.Views_Home_Index.ExecuteAsync() in D:\Projects\MBaumann.Portfolio\src\MBaumann.Portfolio.WebApp\Views\Home\Index.cshtml:line 15

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)

   at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()

--- End of stack trace from previous location where exception was thrown ---

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()

--- End of stack trace from previous location where exception was thrown ---

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)

   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

   at React.AspNet.BabelFileMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

ReacjJS.NET doesn't provide a container for server side rendering, so Emotion try to use the dom and crashed.

Note that Emotion seems to work fine (I don't know it well, I'm certainly missing something) when removing EmotionFunctions from the ~/Views/Home/Index.cshtml view and removing import { renderStylesToString } from 'emotion-server'; and global.EmotionServer = { renderStylesToString }; from Content/component/expose-components.js.

If it helps, I can provide a sample on a new branch/PR, or will be happy to create a PR to fix it if it's a good first issue.

mbaumanndev commented 4 years ago

Note that Emotion seems to work fine (I don't know it well, I'm certainly missing something) when removing EmotionFunctions from the ~/Views/Home/Index.cshtml view and removing import { renderStylesToString } from 'emotion-server'; and global.EmotionServer = { renderStylesToString }; from Content/component/expose-components.js.

I was missing something, it fails on SSR but works fine when in the browser (that makes sense) :

React.Exceptions.ReactServerRenderingException: Error while rendering "RootComponent" to "react_0HLRBIFIRIT9F": TypeError: Unable to get property 'registered' of undefined or null reference

   at Anonymous function (eval code:116:9) ->             throw err;

   at render (eval code:3854:15)

   at read (eval code:3535:11)

   at renderToString (eval code:4244:5)

   at Global code (Script Document [5]:1:1)

 ---> JavaScriptEngineSwitcher.Core.JsRuntimeException: TypeError: Unable to get property 'registered' of undefined or null reference

   at Anonymous function (eval code:116:9) ->             throw err;

   at render (eval code:3854:15)

   at read (eval code:3535:11)

   at renderToString (eval code:4244:5)

   at Global code (Script Document [5]:1:1)

Engine name: ChakraCoreJsEngine

Engine version: 1.11.13

Category: Runtime error

Description: Unable to get property 'registered' of undefined or null reference

Type: TypeError

Document name: eval code

Line number: 116

Column number: 9

Source fragment:             throw err;

Call stack:

   at Anonymous function (eval code:116:9)

   at render (eval code:3854:15)

   at read (eval code:3535:11)

   at renderToString (eval code:4244:5)

   at Global code (Script Document [5]:1:1) ---> JavaScriptEngineSwitcher.ChakraCore.JsRt.JsScriptException: Script threw an exception.

   at JavaScriptEngineSwitcher.ChakraCore.JsRt.JsErrorHelpers.ThrowIfError(JsErrorCode error)

   at JavaScriptEngineSwitcher.ChakraCore.JsRt.JsContext.RunScript(String script, JsSourceContext sourceContext, String sourceUrl, JsParseScriptAttributes& parseAttributes)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.<>c__DisplayClass22_0.<InnerEvaluate>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.<>c__DisplayClass22_0.<InnerEvaluate>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.<>c__DisplayClass10_0`1.<Invoke>b__0()

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.StartThread()

--- End of stack trace from previous location where exception was thrown ---

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.InnnerInvoke(Func`1 del)

   at JavaScriptEngineSwitcher.ChakraCore.ScriptDispatcher.Invoke[T](Func`1 func)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.InnerEvaluate(String expression, String documentName)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.InnerEvaluate[T](String expression, String documentName)

   at JavaScriptEngineSwitcher.ChakraCore.ChakraCoreJsEngine.InnerEvaluate[T](String expression)

   at JavaScriptEngineSwitcher.Core.JsEngineBase.Evaluate[T](String expression)

   at JSPool.PooledJsEngine.Evaluate[T](String expression)

   at React.ReactEnvironment.Execute[T](String code)

   at React.ReactComponent.RenderHtml(TextWriter writer, Boolean renderContainerOnly, Boolean renderServerOnly, Action`3 exceptionHandler, IRenderFunctions renderFunctions)

   --- End of inner exception stack trace ---

   at React.ReactSiteConfiguration.<>c.<.ctor>b__5_0(Exception ex, String ComponentName, String ContainerId)

   at React.ReactComponent.RenderHtml(TextWriter writer, Boolean renderContainerOnly, Boolean renderServerOnly, Action`3 exceptionHandler, IRenderFunctions renderFunctions)

   at React.ReactComponent.<>c__DisplayClass35_0.<RenderHtml>b__0(TextWriter renderHtmlWriter)

   at React.ReactComponent.GetStringFromWriter(Action`1 fnWithTextWriter)

   at React.ReactComponent.RenderHtml(Boolean renderContainerOnly, Boolean renderServerOnly, Action`3 exceptionHandler, IRenderFunctions renderFunctions)

   at React.Router.ReactRouterComponent.RenderRouterWithContext(Boolean renderContainerOnly, Boolean renderServerOnly, IRenderFunctions renderFunctions)

   at React.Router.HtmlHelperExtensions.ReactRouter[T](IHtmlHelper htmlHelper, String componentName, T props, String path, String htmlTag, String containerId, Boolean clientOnly, Boolean serverOnly, String containerClass, Action`2 contextHandler, IRenderFunctions renderFunctions)

   at AspNetCore.Views_Home_Index.ExecuteAsync() in D:\Projects\MBaumann.Portfolio\src\MBaumann.Portfolio.WebApp\Views\Home\Index.cshtml:line 15

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, Boolean invokeViewStarts)

   at Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ActionContext actionContext, IView view, ViewDataDictionary viewData, ITempDataDictionary tempData, String contentType, Nullable`1 statusCode)

   at Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor.ExecuteAsync(ActionContext context, ViewResult result)

   at Microsoft.AspNetCore.Mvc.ViewResult.ExecuteResultAsync(ActionContext context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|21_0(ResourceInvoker invoker, IActionResult result)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()

--- End of stack trace from previous location where exception was thrown ---

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()

--- End of stack trace from previous location where exception was thrown ---

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)

   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

   at React.AspNet.BabelFileMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
dustinsoftware commented 4 years ago

You’re right, EmotionFunctions needs an update. Would you be willing to send a PR?

mbaumanndev commented 4 years ago

I'll send a PR when i'll be done fixing it, it involves to modify the webpack setup, because some code of the emotion cache handler is trimed by webpack

RobertBolender commented 4 years ago

@mbaumanndev were you able to figure out what webpack changes were required to get this to work for you?

dustinsoftware commented 4 years ago

(Hi @RobertBolender 👋)

If you find any docs on updating to emotion 10 relevant to SSR, link them here...

mbaumanndev commented 4 years ago

Hi @RobertBolender, I didin't find how to make it work properly, even with the help of the emotion community on slack.

They gave me this snippet, but it didn't worked :

const path = require('path')
const fs = require('fs')
function findPkgRoot(directory) {
    const configPath = path.join(directory, 'package.json')
    if (fs.existsSync(configPath)) {
        return directory
    }
    return findPkgRoot(path.join(directory, '..'))
}
// webpack config
{
    resolve: {
        alias: ['emotion-server', 'create-emotion-server', /* more */].reduce((aliases, pkgName) => {
            const pkgRoot = findPkgRoot(require.resolve(pkgName))
            const pkgJson = require(path.join(pkgRoot, 'package.json'))
            const pkgMain = pkgJson.module ? pkgJson.module : pkgJson.main
            aliases[pkgName] = path.join(pkgRoot, pkgMain)
            return aliases
        }, {})
    }
}
johot commented 4 years ago

We where just going to start using ReactJS.NET and it worked great until we imported our component library which uses Emotion 10 (@emotion/core). The error we get is that any component that uses the css prop and the emotion jsx function will cause ReactJS.Net to throw an exception:

TypeError: Cannot read property 'key' of null

The reason being this line of code (from Emotion I think):

var insertStyles = function insertStyles(cache, serialized, isStringTag) {
var className = cache.key + "-" + serialized.name;

It seems that the cache object in their implementation is null. I'm not sure what is causing this, shouldn't Emotion 10 be able to work without custom render functions and so on?

According to their documentation here: https://emotion.sh/docs/ssr using the default renderToString function:

import { renderToString } from 'react-dom/server'

should "just work". As far as I can tell Emotion should no longer need access to the head etc since it inserts <style> tags instead.

Is there anyway I can help with more information @dustinsoftware ?

dustinsoftware commented 4 years ago

Interactive debugging should now be possible with the JSEngineSwitcher Node package. You may have luck setting a breakpoint instead of trying to discern from the thrown error.

The reactnet-webpack template uses emotion, if you can throw together a quick repro I can take a look.

You can also skip SSR for some pages with clientOnly: true if that is helpful, or use something like useEffect to defer rendering the style until client side.

On Thu, Mar 19, 2020 at 08:41, Johan Otterud notifications@github.com wrote:

We where just going to start using ReactJS.NET and it worked great until we imported our component library which uses Emotion 10 (@emotion/core). The error we get is that any component that uses the css prop and the emotion jsx function will cause ReactJS.Net to throw an exception:

TypeError: Cannot read property 'key' of null

The reason being this line of code (from Emotion I think):

var insertStyles = function insertStyles(cache, serialized, isStringTag) {var className = cache.key + "-" + serialized.name;

It seems that the cache object in their implementation is null. I'm not sure what is causing this, shouldn't Emotion 10 be able to work without custom render functions and so on?

According to their documentation here: https://emotion.sh/docs/ssr using the default renderToString function:

import { renderToString } from 'react-dom/server'

should "just work". As far as I can tell Emotion should no longer need access to the head etc since it inserts