UNPKG

2.99 kBJavaScriptView Raw
1import React, {useEffect, useLayoutEffect, useContext, useRef, useMemo, useDebugValue} from 'react'
2import {ThemeContext as DefaultThemeContext} from 'theming'
3
4import JssContext from './JssContext'
5import {
6 createStyleSheet,
7 addDynamicRules,
8 updateDynamicRules,
9 removeDynamicRules
10} from './utils/sheets'
11import getSheetIndex from './utils/getSheetIndex'
12import {manageSheet, unmanageSheet} from './utils/managers'
13import getSheetClasses from './utils/getSheetClasses'
14
15function getUseInsertionEffect(isSSR) {
16 return isSSR
17 ? useEffect
18 : React.useInsertionEffect || // React 18+ (https://github.com/reactwg/react-18/discussions/110)
19 useLayoutEffect
20}
21
22const noTheme = {}
23
24const createUseStyles = (styles, options = {}) => {
25 const {index = getSheetIndex(), theming, name, ...sheetOptions} = options
26 const ThemeContext = (theming && theming.context) || DefaultThemeContext
27
28 const useTheme = (theme) => {
29 if (typeof styles === 'function') {
30 return theme || useContext(ThemeContext) || noTheme
31 }
32
33 return noTheme
34 }
35
36 const emptyObject = {}
37
38 return function useStyles(data) {
39 const isFirstMount = useRef(true)
40 const context = useContext(JssContext)
41 const theme = useTheme(data && data.theme)
42
43 const [sheet, dynamicRules] = useMemo(() => {
44 const newSheet = createStyleSheet({
45 context,
46 styles,
47 name,
48 theme,
49 index,
50 sheetOptions
51 })
52
53 if (newSheet && context.isSSR) {
54 // manage immediately during SSRs. browsers will manage the sheet through useInsertionEffect below
55 manageSheet({
56 index,
57 context,
58 sheet: newSheet,
59 theme
60 })
61 }
62
63 return [newSheet, newSheet ? addDynamicRules(newSheet, data) : null]
64 }, [context, theme])
65
66 getUseInsertionEffect(context.isSSR)(() => {
67 // We only need to update the rules on a subsequent update and not in the first mount
68 if (sheet && dynamicRules && !isFirstMount.current) {
69 updateDynamicRules(data, sheet, dynamicRules)
70 }
71 }, [data])
72
73 getUseInsertionEffect(context.isSSR)(() => {
74 if (sheet) {
75 manageSheet({
76 index,
77 context,
78 sheet,
79 theme
80 })
81 }
82
83 return () => {
84 if (sheet) {
85 unmanageSheet({
86 index,
87 context,
88 sheet,
89 theme
90 })
91
92 // when sheet changes, remove related dynamic rules
93 if (dynamicRules) {
94 removeDynamicRules(sheet, dynamicRules)
95 }
96 }
97 }
98 }, [sheet])
99
100 const classes = useMemo(
101 () => (sheet && dynamicRules ? getSheetClasses(sheet, dynamicRules) : emptyObject),
102 [sheet, dynamicRules]
103 )
104
105 useDebugValue(classes)
106
107 useDebugValue(theme === noTheme ? 'No theme' : theme)
108
109 useEffect(() => {
110 isFirstMount.current = false
111 })
112
113 return classes
114 }
115}
116
117export default createUseStyles