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
createCache. So we don't need (or want) the functionality of adding sibling <style> tags.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?
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>
)
})
@grncdr Wow that's all I ever wanted, you're the man!
+1 this would be nice
Most helpful comment
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
EmotionCacheon the server it might already work for what you are doing.The
useEmotionCachehook 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: