Skip to content

Commit 372e9c1

Browse files
kofbehnammodi
andauthored
Migrate withStyles to use hooks (#1508)
* refactor: draft of refactor withStyle * fix: resolve some test case * fix: resolve context issue * fix: remove unused import * fix: pass theme from props instead of actual theme * fix: back hoistNonReactStatics * fix: add props type * use theming.context, improve semantics * createUseStyles accepts theming, not theme * theme from ThemeProvider needs to be passed to useStyles * Refactor withStyles (#1507) * remove noTheme * remove unused type * style: match imported name as same to other files * perf: we do not need initial createUseStyle inside component * clean code * rebase and clean up * fix flow issue * fix(withStyle): remove any type * fix(withStyle): ship any to union * add link for why defaultProps on a function commponen is missing Co-authored-by: Behnam Mohammadi <itten@live.com>
1 parent fdbbbb6 commit 372e9c1

2 files changed

Lines changed: 47 additions & 167 deletions

File tree

packages/react-jss/.size-snapshot.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
{
22
"react-jss.js": {
3-
"bundled": 141808,
4-
"minified": 51596,
5-
"gzipped": 17155
3+
"bundled": 137324,
4+
"minified": 49957,
5+
"gzipped": 16685
66
},
77
"react-jss.min.js": {
8-
"bundled": 108758,
9-
"minified": 41452,
10-
"gzipped": 14271
8+
"bundled": 104274,
9+
"minified": 39816,
10+
"gzipped": 13795
1111
},
1212
"react-jss.cjs.js": {
13-
"bundled": 26225,
14-
"minified": 11318,
15-
"gzipped": 3641
13+
"bundled": 21954,
14+
"minified": 9457,
15+
"gzipped": 3161
1616
},
1717
"react-jss.esm.js": {
18-
"bundled": 24188,
19-
"minified": 9724,
20-
"gzipped": 3418,
18+
"bundled": 20065,
19+
"minified": 7976,
20+
"gzipped": 2945,
2121
"treeshaked": {
2222
"rollup": {
23-
"code": 475,
24-
"import_statements": 417
23+
"code": 426,
24+
"import_statements": 368
2525
},
2626
"webpack": {
27-
"code": 2026
27+
"code": 1945
2828
}
2929
}
3030
}

packages/react-jss/src/withStyles.js

Lines changed: 32 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,71 @@
11
// @flow
22
import * as React from 'react'
33
import hoistNonReactStatics from 'hoist-non-react-statics'
4-
import {type StyleSheet, type Classes} from 'jss'
5-
import {ThemeContext} from 'theming'
4+
import {type Classes} from 'jss'
5+
import {ThemeContext as DefaultThemeContext} from 'theming'
66

7-
import type {HOCProps, HOCOptions, Styles, InnerProps, DynamicRules} from './types'
7+
import type {HOCProps, HOCOptions, Styles, InnerProps} from './types'
88
import getDisplayName from './getDisplayName'
99
import memoize from './utils/memoizeOne'
1010
import mergeClasses from './utils/mergeClasses'
11-
import JssContext from './JssContext'
1211
import getSheetIndex from './utils/getSheetIndex'
13-
import {
14-
createStyleSheet,
15-
updateDynamicRules,
16-
addDynamicRules,
17-
removeDynamicRules
18-
} from './utils/sheets'
19-
import {manageSheet, unmanageSheet} from './utils/managers'
20-
import getSheetClasses from './utils/getSheetClasses'
21-
22-
interface State {
23-
dynamicRules: ?DynamicRules;
24-
sheet: ?StyleSheet;
25-
classes: {};
26-
}
12+
import createUseStyles from './createUseStyles'
2713

2814
const NoRenderer = (props: {children?: React.Node}) => props.children || null
2915

30-
const noTheme = {}
31-
32-
type CreateWithStyles = <Theme>(
33-
Styles<Theme>,
34-
HOCOptions<Theme> | void
35-
) => <Props: InnerProps>(React.ComponentType<Props>) => React.ComponentType<Props>
16+
type CreateWithStyles = <Theme: {}>(Styles<Theme>, HOCOptions<Theme> | void) => any => Classes
3617

3718
/**
3819
* HOC creator function that wrapps the user component.
3920
*
4021
* `withStyles(styles, [options])(Component)`
4122
*/
23+
4224
const createWithStyles: CreateWithStyles = <Theme>(styles, options = {}) => {
4325
const {index = getSheetIndex(), theming, injectTheme, ...sheetOptions} = options
44-
const isThemingEnabled = typeof styles === 'function'
45-
const ThemeConsumer = (theming && theming.context.Consumer) || ThemeContext.Consumer
26+
const ThemeContext = theming ? theming.context : DefaultThemeContext
4627

4728
return <Props: InnerProps>(InnerComponent = NoRenderer) => {
4829
const displayName = getDisplayName(InnerComponent)
4930

50-
const getTheme = (props): Theme => (isThemingEnabled ? props.theme : ((noTheme: any): Theme))
51-
52-
class WithStyles extends React.Component<HOCProps<Theme, Props>, State> {
53-
static displayName = `WithStyles(${displayName})`
31+
const mergeClassesProp = memoize(
32+
(sheetClasses, classesProp): Classes =>
33+
classesProp ? mergeClasses(sheetClasses, classesProp) : sheetClasses
34+
)
5435

55-
// $FlowFixMe[prop-missing]
56-
static defaultProps = {...InnerComponent.defaultProps}
36+
const hookOptions = Object.assign((sheetOptions: any), {
37+
theming,
38+
index,
39+
name: displayName
40+
})
5741

58-
static createState(props) {
59-
const sheet = createStyleSheet({
60-
styles,
61-
theme: getTheme(props),
62-
index,
63-
name: displayName,
64-
context: props.jssContext,
65-
sheetOptions
66-
})
42+
const useStyles = createUseStyles(styles, hookOptions)
6743

68-
if (!sheet) {
69-
return {classes: {}, dynamicRules: undefined, sheet: undefined}
70-
}
44+
const WithStyles = React.forwardRef((props: HOCProps<Theme, Props>, ref) => {
45+
const theme = React.useContext(ThemeContext)
7146

72-
const dynamicRules = addDynamicRules(sheet, props)
47+
const newProps: Props & {theme: any} = {...props}
7348

74-
return {
75-
sheet,
76-
dynamicRules,
77-
classes: getSheetClasses(sheet, dynamicRules)
78-
}
49+
if (injectTheme && newProps.theme == null) {
50+
newProps.theme = theme
7951
}
8052

81-
static manage(props, state) {
82-
const {sheet} = state
83-
if (sheet) {
84-
manageSheet({
85-
sheet,
86-
index,
87-
context: props.jssContext,
88-
theme: getTheme(props)
89-
})
90-
}
91-
}
53+
const sheetClasses = useStyles(newProps)
9254

93-
static unmanage(props, state) {
94-
const {sheet, dynamicRules} = state
95-
96-
if (sheet) {
97-
unmanageSheet({
98-
context: props.jssContext,
99-
index,
100-
sheet,
101-
theme: getTheme(props)
102-
})
103-
104-
if (dynamicRules) {
105-
removeDynamicRules(sheet, dynamicRules)
106-
}
107-
}
108-
}
55+
const classes = mergeClassesProp(sheetClasses, props.classes)
10956

110-
mergeClassesProp = memoize(
111-
(sheetClasses, classesProp): Classes =>
112-
classesProp ? mergeClasses(sheetClasses, classesProp) : sheetClasses
113-
)
57+
return <InnerComponent {...newProps} classes={classes} ref={ref} />
58+
})
11459

115-
constructor(props: HOCProps<Theme, Props>) {
116-
super(props)
60+
WithStyles.displayName = `WithStyles(${displayName})`
11761

118-
this.state = WithStyles.createState(props)
119-
const {registry} = props.jssContext
120-
const {sheet} = this.state
121-
if (sheet && registry) {
122-
registry.add(sheet)
123-
}
124-
}
62+
// $FlowFixMe[prop-missing] https://github.com/facebook/flow/issues/7467
63+
WithStyles.defaultProps = {...InnerComponent.defaultProps}
12564

126-
componentDidMount() {
127-
const {props, state} = this
128-
if (props && state) {
129-
WithStyles.manage(props, state)
130-
}
131-
}
65+
// $FlowFixMe[prop-missing]
66+
WithStyles.InnerComponent = InnerComponent
13267

133-
componentDidUpdate(prevProps: HOCProps<Theme, Props>, prevState: State) {
134-
if (isThemingEnabled && this.props.theme !== prevProps.theme) {
135-
const newState = WithStyles.createState(this.props)
136-
WithStyles.manage(this.props, newState)
137-
WithStyles.unmanage(prevProps, prevState)
138-
139-
// eslint-disable-next-line react/no-did-update-set-state
140-
this.setState(newState)
141-
} else if (this.state.sheet && this.state.dynamicRules) {
142-
// Only update the rules when we don't generate a new sheet
143-
updateDynamicRules(this.props, this.state.sheet, this.state.dynamicRules)
144-
}
145-
}
146-
147-
componentWillUnmount() {
148-
WithStyles.unmanage(this.props, this.state)
149-
}
150-
151-
render() {
152-
const {innerRef, jssContext, theme, classes, ...rest} = this.props
153-
const {classes: sheetClasses} = this.state
154-
const props = {
155-
...rest,
156-
classes: this.mergeClassesProp(sheetClasses, classes)
157-
}
158-
159-
if (innerRef) props.ref = innerRef
160-
if (injectTheme) props.theme = theme
161-
162-
return <InnerComponent {...props} />
163-
}
164-
}
165-
166-
const JssContextSubscriber = React.forwardRef((props, ref) => (
167-
<JssContext.Consumer>
168-
{context => {
169-
if (isThemingEnabled || injectTheme) {
170-
return (
171-
<ThemeConsumer>
172-
{theme => (
173-
<WithStyles innerRef={ref} theme={theme} {...props} jssContext={context} />
174-
)}
175-
</ThemeConsumer>
176-
)
177-
}
178-
179-
return <WithStyles innerRef={ref} {...props} jssContext={context} theme={noTheme} />
180-
}}
181-
</JssContext.Consumer>
182-
))
183-
184-
JssContextSubscriber.displayName = `JssContextSubscriber(${displayName})`
185-
// $FlowFixMe[prop-missing] - React's types should allow custom static properties on component.
186-
JssContextSubscriber.InnerComponent = InnerComponent
187-
188-
return hoistNonReactStatics(JssContextSubscriber, InnerComponent)
68+
return hoistNonReactStatics(WithStyles, InnerComponent)
18969
}
19070
}
19171

0 commit comments

Comments
 (0)