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

SSR doesn't work with multiple emotion instances. #603

Closed
whmountains opened this issue Mar 20, 2018 · 1 comment · Fixed by #605
Closed

SSR doesn't work with multiple emotion instances. #603

whmountains opened this issue Mar 20, 2018 · 1 comment · Fixed by #605
Assignees
Labels

Comments

@whmountains
Copy link

whmountains commented Mar 20, 2018

Versions

  • emotion version: 9.0.2
  • react version: N/A

Code

const createEmotion = require('create-emotion')
const createEmotionServer = require('create-emotion-server')

const context = {}

// instances should work together because they share context
const emotion1 = createEmotion(context)
const emotion2 = createEmotion(context)

// emotionServer can be bootstrapped from either instance.
const { renderStylesToString } = createEmotionServer(emotion1)

// Rendering works fine on components created by the first instance
const className1 = emotion1.css`color: magenta;`
console.log(renderStylesToString(`<h1 className="${className1}"></h1>`))
// <style data-emotion-css="madiid">.css-madiid{color:magenta;}</style><h1 className="css-madiid"></h1>

// Style tags are empty when rendering components created by the first instance
const className2 = emotion2.css`color: rebeccapurple;`
console.log(renderStylesToString(`<h1 className="${className2}"></h1>`))
// <style data-emotion-css="l7wj9y"></style><h1 className="css-l7wj9y"></h1>

Steps to reproduce

Tried to use emotion server-side rendering with react-static. Turns out it triggered an edge case in emotion.

To be clear, react-static is not required to reproduce the bug. Steps to reproduce:

  1. Create two instances of emotion that share a single context object. I did this following the example in the docs.
  2. Create a css class using css exported from the second instance.
  3. Interpolate the class into some html.
  4. Try to inline styles into the html using renderStylesToString

Problem description

  • renderStylesToString creates style tags with empty contents.
  • The style tags are empty because emotion.caches.inserted[id] is an empty string. (source)
  • emotion.caches.inserted[id] is an empty string because current never get's updated with the css rules generated by stylus via stylis-rule-sheet (You can test this yourself by adding console.log(current) after this line).
  • current never gets updated because the insertRule function has captured a closure referencing current from the first invocation of createEmotion. (Sorry if I'm not using the right technical terms. I'm not used to discussing the innards of JS.)
  • The closure points to the current variable from the first instance because it's used by a stylus plugin that is only registered if caches has not been created yet. (i.e. the first invocation of createEmotion) (source)

Runable example

Minimal test-case here: https://github.com/whmountains/emotion-bug-repro
Try online at: https://codesandbox.io/s/github/whmountains/emotion-bug-repro/tree/master/?expanddevtools=1&view=editor

Temporary workaround

I think this will work, though I haven't tested it.

Replace this:

import createEmotion from 'create-emotion'

const context = typeof global !== 'undefined' ? global : {}

export const {
  flush,
  hydrate,
  cx,
  merge,
  getRegisteredStyles,
  injectGlobal,
  keyframes,
  css,
  sheet,
  caches
} = createEmotion(context)

With this:

import createEmotion from 'create-emotion'

const world = global !== 'undefined' ? global : {}
const emotion = world.__EMOTION_INSTANCE__
  ? world.__EMOTION_INSTANCE__
  : (world.__EMOTION_INSTANCE__ = createEmotion({}))

export const {
  flush,
  hydrate,
  cx,
  merge,
  getRegisteredStyles,
  injectGlobal,
  keyframes,
  css,
  sheet,
  caches
} = emotion

Suggested Solutions

I have a few ideas. Haven't tested any of them.

  1. Move let current outside the createEmotion function.

  2. Keep current inside the context object.

  3. Get stylus and the stylesheet manager out of the context object.

@emmatown emmatown added the bug label Mar 20, 2018
@emmatown emmatown self-assigned this Mar 20, 2018
@emmatown
Copy link
Member

@whmountains Thanks for the awesome issue!!

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

Successfully merging a pull request may close this issue.

2 participants