question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

A hook version of <ClassNames> for power users

See original GitHub issue

Preface: I know this topic has already been brought up a few times (#1295, #967, #1321) but I have a use-case that I haven’t seen mentioned before, and a proposed solution that side-steps the inherent complexity of this problem, while giving power-users what they need.

The problem

  1. Our stack already uses a jsx pragma and so we cannot use Emotion’s.
  2. At the same time we do server-side rendering with createCache. So we don’t need (or want) the functionality of adding sibling <style> tags.
  3. We really need the ability to convert style objects to class names with just hooks, no render-prop components, because:
    1. we have hooks that compose hooks that compose hooks, and components have no place there.
    2. render-prop components mess with the es-lint hooks plugin when a hook is needed inside the render function.

The main issue as I understand it is that most people are going to need the version of Emotion that adds <style> tags next to the component that rendered them, so a hook is out of the question as it can’t do that (at least not in a clean way). And exposing a hook that’s only useful if you roll your own server-side rendering isn’t ideal…

Proposed solution

So I propose that instead of Emotion supplying users with a potentially confusing hook API, just expose some of the functionality that would be needed to create it (the guts of ClassNames) and let us power-users roll our own. That way we’re not leading less-informed users astray from the pit of success, but also letting more advanced users integrate Emotion into their existing stack without pain.

I’d be happy to take a stab at a PR that makes the minimal changes necessary to move this problem into ‘user-land’. Is that something you’d be willing to consider merging?

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:18
  • Comments:5

github_iconTop GitHub Comments

5reactions
grncdrcommented, May 4, 2020

Hi @oztune it might (or might not) be interesting to you, but I have written a hook that does this already. For my use-case server side rendering wasn’t needed, but if you are able to get access to an EmotionCache on the server it might already work for what you are doing.

import { CSSInterpolation, serializeStyles } from '@emotion/serialize'
import { insertStyles } from '@emotion/utils'
import { useCallback } from 'react'

import { useEmotionCache } from 'ds/styled' // see below

/**
 * **This hook only works for browser rendering!** (I wrote it for separating
 * modal behaviour from modal styles).
 */
export function useCssClassName(): (...args: Array<CSSInterpolation>) => string {
  const cache = useEmotionCache()
  return useCallback(
    (...args) => {
      if (!cache) {
        if (process.env.NODE_ENV === 'production') {
          return 'emotion-cache-missing'
        }
        throw new Error('No emotion cache found!')
      }
      const serialized = serializeStyles(args, cache.registered)
      insertStyles(cache, serialized, false)
      return cache.key + '-' + serialized.name
    },
    [cache],
  )
}

The useEmotionCache hook is specific to our application, and it sounds like you’re already in a position to implement your own version. Because I didn’t need SSR for this hook, my version just exposes the default emotion cache in a custom context:

import React from 'react'
import { EmotionCache, withEmotionCache } from '@emotion/react'

const CacheContext = createContext<EmotionCache|undefined>(undefined)
export const useEmotionCache = () => useContext(CacheContext)

// We wrap our App with this
export const CacheProvider = withEmotionCache((
  { children }: { children: React.ReactNode },
  cache: EmotionCache
) => {
  return (
    <CacheContext.Provider value={cache}>
      {children}
   </CacheContext.Provider>
  )
})
4reactions
sarayourfriendcommented, Jul 2, 2021

I actually have a similar solution but just for a cx function. It’s available here as a codesandbox: https://codesandbox.io/s/solitary-hooks-l8b03?file=/src/useCx.js

I asked in the Emotion Slack workspace whether it’s worth an upstream PR to add this to Emotion’s @emotion/react.

We have a similar situation in the https://github.com/WordPress/Gutenberg repository:

  • We do not want to use the jsx pragma
  • We do not want to refactor everything to use @emotion/styled and we prefer the css and hook based approach we’ve already taken

Furthermore, we utilize iframes in Gutenberg and need a solution that hooks into the Emotion cache.

We’re in a similar situation here as well:

we have hooks that compose hooks that compose hooks, and components have no place there.

A TypeScript version of the hook exists here: https://github.com/WordPress/gutenberg/pull/33172/files#diff-f2e01286efb7fd67eb692fb1528160c4450145949d6e4f7daf5b1c6a205453d4

Like I said in Slack, happy to open a PR to add this functionality to @emotion/react. I think it’d be a great additional API that expands the use cases for Emotion.

Note: My approach, I believe, is rather naïve so I’m hoping for feedback from maintainers about whether this approach is viable and if there are modifications that need to be made. All my research into the Emotion source code says that it should be possible to accomplish things this way and I can’t pinpoint any real issues with what I’ve done but I’d expect there to be at least some performance considerations that I haven’t taken into account.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Applying React Conditional classNames - Pluralsight
Depending on your preferences and needs, you can apply conditional classNames with a ternary or with a library.
Read more >
Using hooks to set menu item className in react
I am trying to create a simple menu using react and I can't for the life of me work this out. Please see...
Read more >
classnames - npm
A simple utility for conditionally joining classNames together. Latest version: 2.3.2, last published: 4 months ago. Start using classnames ...
Read more >
The Guide to Learning React Hooks (Examples & Tutorials)
Learn all about React Hooks with this hands-on guide. Includes tutorials and code examples on using hooks for state and effects, for context...
Read more >
Advanced React Hooks: Creating custom reusable Hooks
Advanced React Hooks: Creating custom reusable Hooks ; In this article, we'll look at practical applications of the reusable Hook pattern. As we ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found