22/* eslint-disable react/destructuring-assignment */
33import React , { Component , type ComponentType } from 'react'
44import PropTypes from 'prop-types'
5- import defaultTheming from 'theming'
5+ import { withTheme } from 'theming'
66import type { StyleSheet } from 'jss'
77import jss , { getDynamicStyles , SheetsManager } from './jss'
88import compose from './compose'
99import getDisplayName from './getDisplayName'
1010import * as ns from './ns'
1111import contextTypes from './contextTypes'
12- import type {
13- Options ,
14- Theme ,
15- StylesOrCreator ,
16- InnerProps ,
17- OuterProps ,
18- Context ,
19- SubscriptionId
20- } from './types'
12+ import type { Options , Theme , StylesOrCreator , InnerProps , OuterProps , Context } from './types'
2113
2214const env = process . env . NODE_ENV
2315
@@ -44,11 +36,11 @@ const dynamicStylesNs = Math.random()
4436 *
4537 */
4638
47- type State = {
48- theme : Theme ,
49- dynamicSheet ? : StyleSheet ,
39+ type State = { |
40+ dynamicSheet : StyleSheet | null ,
41+ staticSheet : StyleSheet | null ,
5042 classes : { }
51- }
43+ | }
5244
5345const getStyles = ( stylesOrCreator : StylesOrCreator , theme : Theme ) => {
5446 if ( typeof stylesOrCreator !== 'function' ) {
@@ -85,56 +77,56 @@ export default function createHOC<
8577 options : Options
8678) : ComponentType < OuterPropsType > {
8779 const isThemingEnabled = typeof stylesOrCreator === 'function'
88- const { theming = defaultTheming , inject, jss : optionsJss , ...sheetOptions } = options
80+ const { theming , inject, jss : optionsJss , ...sheetOptions } = options
8981 const injectMap = inject ? toMap ( inject ) : defaultInjectProps
90- const { themeListener} = theming
9182 const displayName = getDisplayName ( InnerComponent )
9283 const defaultClassNamePrefix = env === 'production' ? '' : `${ displayName } -`
9384 const noTheme = { }
9485 const managerId = managersCounter ++
9586 const manager = new SheetsManager ( )
96- // $FlowFixMe defaultProps are not defined in React$Component
97- const defaultProps : InnerPropsType = { ... InnerComponent . defaultProps }
98- delete defaultProps . classes
87+
88+ // $FlowFixMe: DefaultProps is missing in type definitions
89+ const { classes : defaultClasses = { } , ... defaultProps } = { ... InnerComponent . defaultProps }
9990
10091 class Jss extends Component < OuterPropsType , State > {
10192 static displayName = `Jss(${ displayName } )`
10293
10394 static InnerComponent = InnerComponent
10495
105- static contextTypes = {
106- ...contextTypes ,
107- ...( isThemingEnabled ? themeListener . contextTypes : { } )
108- }
96+ static contextTypes = contextTypes
10997
11098 static propTypes = {
11199 innerRef : PropTypes . func
112100 }
113101
114- static defaultProps = defaultProps
102+ static defaultProps = {
103+ ...defaultProps ,
104+ theme : noTheme
105+ }
106+
107+ classNamePrefix : string = defaultClassNamePrefix
115108
116109 constructor ( props : OuterPropsType , context : Context ) {
117110 super ( props , context )
118- const theme = isThemingEnabled ? themeListener . initial ( context ) : noTheme
119111
120- this . state = this . createState ( { theme, classes : { } } , props )
121- this . manage ( this . state )
122- }
112+ const contextSheetOptions = context [ ns . sheetOptions ]
123113
124- componentDidMount ( ) {
125- if ( isThemingEnabled ) {
126- this . unsubscribeId = themeListener . subscribe ( this . context , this . setTheme )
114+ if ( contextSheetOptions && contextSheetOptions . classNamePrefix ) {
115+ this . classNamePrefix = contextSheetOptions . classNamePrefix + this . classNamePrefix
127116 }
117+
118+ this . state = this . createState ( )
119+ this . manage ( this . state )
128120 }
129121
130122 componentDidUpdate ( prevProps : OuterPropsType , prevState : State ) {
131123 const { dynamicSheet } = this . state
132124 if ( dynamicSheet ) dynamicSheet . update ( this . props )
133125
134- if ( isThemingEnabled && this . state . theme !== prevState . theme ) {
135- const newState = this . createState ( this . state , this . props )
126+ if ( isThemingEnabled && this . props . theme !== prevProps . theme ) {
127+ const newState = this . createState ( )
136128 this . manage ( newState )
137- this . manager . unmanage ( prevState . theme )
129+ this . manager . unmanage ( prevProps . theme )
138130 // eslint-disable-next-line react/no-did-update-set-state
139131 this . setState ( newState )
140132 }
@@ -146,17 +138,15 @@ export default function createHOC<
146138 }
147139
148140 componentWillUnmount ( ) {
149- if ( isThemingEnabled && this . unsubscribeId ) {
150- themeListener . unsubscribe ( this . context , this . unsubscribeId )
151- }
152-
153- this . manager . unmanage ( this . state . theme )
141+ this . manager . unmanage ( this . theme )
154142 if ( this . state . dynamicSheet ) {
155143 this . jss . removeStyleSheet ( this . state . dynamicSheet )
156144 }
157145 }
158146
159- setTheme = ( theme : Theme ) => this . setState ( { theme} )
147+ get theme ( ) {
148+ return isThemingEnabled ? this . props . theme : noTheme
149+ }
160150
161151 get jss ( ) {
162152 return this . context [ ns . jss ] || optionsJss || jss
@@ -177,92 +167,99 @@ export default function createHOC<
177167 return manager
178168 }
179169
180- manage ( { theme, dynamicSheet} : State ) {
181- const contextSheetOptions = this . context [ ns . sheetOptions ]
182- if ( contextSheetOptions && contextSheetOptions . disableStylesGeneration ) {
183- return
184- }
185- const registry = this . context [ ns . sheetsRegistry ]
186-
187- const staticSheet = this . manager . manage ( theme )
188- if ( registry ) registry . add ( staticSheet )
170+ getStaticSheet ( ) {
171+ const theme = this . theme
172+ let staticSheet = this . manager . get ( theme )
189173
190- if ( dynamicSheet ) {
191- dynamicSheet . update ( this . props ) . attach ( )
192- if ( registry ) registry . add ( dynamicSheet )
174+ if ( staticSheet ) {
175+ return staticSheet
193176 }
194- }
195177
196- createState ( state : State , { classes : userClasses } ) : State {
197178 const contextSheetOptions = this . context [ ns . sheetOptions ]
198- if ( contextSheetOptions && contextSheetOptions . disableStylesGeneration ) {
199- return { ...state , classes : { } }
200- }
201-
202- let classNamePrefix = defaultClassNamePrefix
203- let staticSheet = this . manager . get ( state . theme )
204-
205- if ( contextSheetOptions && contextSheetOptions . classNamePrefix ) {
206- classNamePrefix = contextSheetOptions . classNamePrefix + classNamePrefix
207- }
208-
209- if ( ! staticSheet ) {
210- const styles = getStyles ( stylesOrCreator , state . theme )
211- staticSheet = this . jss . createStyleSheet ( styles , {
212- ...sheetOptions ,
213- ...contextSheetOptions ,
214- meta : `${ displayName } , ${ isThemingEnabled ? 'Themed' : 'Unthemed' } , Static` ,
215- classNamePrefix
216- } )
217- this . manager . add ( state . theme , staticSheet )
218- // $FlowFixMe Cannot add random fields to instance of class StyleSheet
219- staticSheet [ dynamicStylesNs ] = getDynamicStyles ( styles )
220- }
179+ const styles = getStyles ( stylesOrCreator , theme )
180+ staticSheet = this . jss . createStyleSheet ( styles , {
181+ ...sheetOptions ,
182+ ...contextSheetOptions ,
183+ meta : `${ displayName } , ${ isThemingEnabled ? 'Themed' : 'Unthemed' } , Static` ,
184+ classNamePrefix : this . classNamePrefix
185+ } )
186+ this . manager . add ( theme , staticSheet )
187+ // $FlowFixMe Cannot add random fields to instance of class StyleSheet
188+ staticSheet [ dynamicStylesNs ] = getDynamicStyles ( styles )
189+
190+ return staticSheet
191+ }
221192
193+ getDynamicSheet ( staticSheet ) {
222194 // $FlowFixMe Cannot access random fields on instance of class StyleSheet
223195 const dynamicStyles = staticSheet [ dynamicStylesNs ]
224- let dynamicSheet
225196
226197 if ( dynamicStyles ) {
227- dynamicSheet = this . jss . createStyleSheet ( dynamicStyles , {
198+ const contextSheetOptions = this . context [ ns . sheetOptions ]
199+
200+ return this . jss . createStyleSheet ( dynamicStyles , {
228201 ...sheetOptions ,
229202 ...contextSheetOptions ,
230203 meta : `${ displayName } , ${ isThemingEnabled ? 'Themed' : 'Unthemed' } , Dynamic` ,
231- classNamePrefix,
204+ classNamePrefix : this . classNamePrefix ,
232205 link : true
233206 } )
234207 }
235208
236- // $FlowFixMe InnerComponent can be class or stateless, the latter doesn't have a defaultProps property
237- const defaultClasses = InnerComponent . defaultProps
238- ? InnerComponent . defaultProps . classes
239- : undefined
209+ return null
210+ }
211+
212+ manage ( { dynamicSheet, staticSheet} : State ) {
213+ const registry = this . context [ ns . sheetsRegistry ]
214+
215+ this . manager . manage ( this . theme )
216+ if ( staticSheet && registry ) registry . add ( staticSheet )
217+
218+ if ( dynamicSheet !== null ) {
219+ dynamicSheet . update ( this . props ) . attach ( )
220+ if ( registry ) registry . add ( dynamicSheet )
221+ }
222+ }
223+
224+ computeClasses ( staticSheet , dynamicSheet ) {
240225 const jssClasses = dynamicSheet
241226 ? compose (
242227 staticSheet . classes ,
243228 dynamicSheet . classes
244229 )
245230 : staticSheet . classes
246- const classes = {
231+ return {
247232 ...defaultClasses ,
248233 ...jssClasses ,
249- ...userClasses
234+ ...this . props . classes
250235 }
251-
252- return { theme : state . theme , dynamicSheet, classes}
253236 }
254237
255- unsubscribeId: SubscriptionId
238+ createState ( ) : State {
239+ const contextSheetOptions = this . context [ ns . sheetOptions ]
240+ if ( contextSheetOptions && contextSheetOptions . disableStylesGeneration ) {
241+ return {
242+ classes : { } ,
243+ dynamicSheet : null ,
244+ staticSheet : null
245+ }
246+ }
247+
248+ const staticSheet = this . getStaticSheet ( )
249+ const dynamicSheet = this . getDynamicSheet ( staticSheet )
250+
251+ return { staticSheet, dynamicSheet, classes : this . computeClasses ( staticSheet , dynamicSheet ) }
252+ }
256253
257254 context: Context
258255
259256 render ( ) {
260- const { theme , dynamicSheet, classes} = this . state
261- const { innerRef, ...props } : OuterPropsType = this . props
262- const sheet = dynamicSheet || this . manager . get ( theme )
257+ const { dynamicSheet, classes, staticSheet } = this . state
258+ const { innerRef, theme , ...props } : OuterPropsType = this . props
259+ const sheet = dynamicSheet || staticSheet
263260
264- if ( injectMap . sheet && ! props . sheet ) props . sheet = sheet
265- if ( isThemingEnabled && injectMap . theme && ! props . theme ) props . theme = theme
261+ if ( injectMap . sheet && ! props . sheet && sheet ) props . sheet = sheet
262+ if ( injectMap . theme ) props . theme = theme
266263
267264 // We have merged classes already.
268265 if ( injectMap . classes ) props . classes = classes
@@ -271,5 +268,11 @@ export default function createHOC<
271268 }
272269 }
273270
271+ if ( isThemingEnabled || injectMap . theme ) {
272+ return theming
273+ ? theming . withTheme ( Jss , { forwardInnerRef : true } )
274+ : withTheme ( Jss , { forwardInnerRef : true } )
275+ }
276+
274277 return Jss
275278}
0 commit comments