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

React 18 support for Global Styles :) #2574

Closed
wants to merge 9 commits into from
Closed

React 18 support for Global Styles :) #2574

wants to merge 9 commits into from

Conversation

sanjaiyan-dev
Copy link

What:

Hi,

React 18 introduced new useInsertionEffect as substitution for useLayourEffect for better hydration supports for css in js libraries.

Emotion CSS (React) global styles uses useLayoutEffect which we can change to useInsertionEffect and fallback to useLayoutEffect.

Why:

This will give better concurrent support and hydration support for websites using React 18 :)

How:

By changing-:

const useInsertionLayout = React.useInsertionEffect || React.useLayoutEffect

and

 useInsertionLayout(() => {
      let sheetRefCurrent = (sheetRef.current: any)
      let [sheet, rehydrating] = sheetRefCurrent
      if (rehydrating) {
        sheetRefCurrent[1] = false
        return
      }
      if (serialized.next !== undefined) {
        // insert keyframes
        insertStyles(cache, serialized.next, true)
      }

      if (sheet.tags.length) {
        // if this doesn't exist then it will be null so the style element will be appended
        let element = sheet.tags[sheet.tags.length - 1].nextElementSibling
        sheet.before = ((element: any): Element | null)
        sheet.flush()
      }
      cache.insert(``, serialized, sheet, false)
    }, [cache, serialized.name])

Code need to be changed like below one 👇 (Emotion React Global Styles)-:

// @flow
import * as React from 'react'
import { withEmotionCache } from './context'
import { ThemeContext } from './theming'
import { insertStyles } from '@emotion/utils'
import { isBrowser } from './utils'

import { StyleSheet } from '@emotion/sheet'
import { serializeStyles } from '@emotion/serialize'

type Styles = Object | Array<Object>

type GlobalProps = {
  +styles: Styles | (Object => Styles)
}

let warnedAboutCssPropForGlobal = false

const useInsertionLayout = React.useInsertionEffect || React.useLayoutEffect

// maintain place over rerenders.
// initial render from browser, insertBefore context.sheet.tags[0] or if a style hasn't been inserted there yet, appendChild
// initial client-side render from SSR, use place of hydrating tag

export let Global: React.AbstractComponent<GlobalProps> =
  /* #__PURE__ */ withEmotionCache((props: GlobalProps, cache) => {
    if (
      process.env.NODE_ENV !== 'production' &&
      !warnedAboutCssPropForGlobal && // check for className as well since the user is
      // probably using the custom createElement which
      // means it will be turned into a className prop
      // $FlowFixMe I don't really want to add it to the type since it shouldn't be used
      (props.className || props.css)
    ) {
      console.error(
        "It looks like you're using the css prop on Global, did you mean to use the styles prop instead?"
      )
      warnedAboutCssPropForGlobal = true
    }
    let styles = props.styles

    let serialized = serializeStyles(
      [styles],
      undefined,
      React.useContext(ThemeContext)
    )

    if (!isBrowser) {
      let serializedNames = serialized.name
      let serializedStyles = serialized.styles
      let next = serialized.next
      while (next !== undefined) {
        serializedNames += ' ' + next.name
        serializedStyles += next.styles
        next = next.next
      }

      let shouldCache = cache.compat === true

      let rules = cache.insert(
        ``,
        { name: serializedNames, styles: serializedStyles },
        cache.sheet,
        shouldCache
      )

      if (shouldCache) {
        return null
      }

      return (
        <style
          {...{
            [`data-emotion`]: `${cache.key}-global ${serializedNames}`,
            dangerouslySetInnerHTML: { __html: rules },
            nonce: cache.sheet.nonce
          }}
        />
      )
    }

    // yes, i know these hooks are used conditionally
    // but it is based on a constant that will never change at runtime
    // it's effectively like having two implementations and switching them out
    // so it's not actually breaking anything

    let sheetRef = React.useRef()

    useInsertionLayout(() => {
      const key = `${cache.key}-global`

      let sheet = new StyleSheet({
        key,
        nonce: cache.sheet.nonce,
        container: cache.sheet.container,
        speedy: cache.sheet.isSpeedy
      })
      let rehydrating = false
      // $FlowFixMe
      let node: HTMLStyleElement | null = document.querySelector(
        `style[data-emotion="${key} ${serialized.name}"]`
      )
      if (cache.sheet.tags.length) {
        sheet.before = cache.sheet.tags[0]
      }
      if (node !== null) {
        rehydrating = true
        // clear the hash so this node won't be recognizable as rehydratable by other <Global/>s
        node.setAttribute('data-emotion', key)
        sheet.hydrate([node])
      }
      sheetRef.current = [sheet, rehydrating]
      return () => {
        sheet.flush()
      }
    }, [cache])

    useInsertionLayout(() => {
      let sheetRefCurrent = (sheetRef.current: any)
      let [sheet, rehydrating] = sheetRefCurrent
      if (rehydrating) {
        sheetRefCurrent[1] = false
        return
      }
      if (serialized.next !== undefined) {
        // insert keyframes
        insertStyles(cache, serialized.next, true)
      }

      if (sheet.tags.length) {
        // if this doesn't exist then it will be null so the style element will be appended
        let element = sheet.tags[sheet.tags.length - 1].nextElementSibling
        sheet.before = ((element: any): Element | null)
        sheet.flush()
      }
      cache.insert(``, serialized, sheet, false)
    }, [cache, serialized.name])

    return null
  })

if (process.env.NODE_ENV !== 'production') {
  Global.displayName = 'EmotionGlobal'
}

Checklist:

  • Documentation
  • Tests
  • Code complete
  • Changeset

Hi sir/madam I am new to JavaScript world even to programming world Please apologize if I made any mistakes :)

emmatown and others added 7 commits February 10, 2021 08:30
…1 is hoisted (#2248)

* import css from core to ensure correct import even when emotion/css@11 is hoisted

* Update index.js

Co-authored-by: Mitchell Hamilton <mitchell@hamil.town>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* v10: Drop usage of deprecated React types

* Adjust test-only references to "SFC" terminology

* Tweak changesets

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
* Fix hydration mismatches with `React.useId`

* Update .changeset/strange-kids-change.md

Co-authored-by: Mitchell Hamilton <mitchell@hamil.town>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@changeset-bot
Copy link

changeset-bot bot commented Dec 5, 2021

⚠️ No Changeset found

Latest commit: fe73ed9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR%0A)

@Andarist
Copy link
Member

Andarist commented Dec 5, 2021

We'll be working on the React 18 support soon. For the time being, we don't expect to merge any PRs related to this topic - unless the PR would really implement the whole thing in a comprehensive manner. I'm sorry if this sounds harsh but it will just be easier this way to manage the whole thing for us

@sanjaiyan-dev
Copy link
Author

We'll be working on the React 18 support soon. For the time being, we don't expect to merge any PRs related to this topic - unless the PR would really implement the whole thing in a comprehensive manner. I'm sorry if this sounds harsh but it will just be easier this way to manage the whole thing for us

Ok :)

Methuselah96 and others added 2 commits December 8, 2021 09:25
* Export Keyframes type from @emotion/core

* Create quiet-impalas-speak.md

* Update quiet-impalas-speak.md

* Update quiet-impalas-speak.md

* Update .changeset/quiet-impalas-speak.md

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@Andarist
Copy link
Member

Andarist commented Apr 9, 2022

If I understand correctly the same(ish) thing has landed in #2600

@Andarist Andarist closed this Apr 9, 2022
@sanjaiyan-dev
Copy link
Author

Yh 🙌🔥🙌

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

Successfully merging this pull request may close these issues.

None yet

6 participants