Skip to content

Commit

Permalink
fix: make useStyles work in strict mode (cssinjs#1648)
Browse files Browse the repository at this point in the history
  • Loading branch information
ernestostifano committed Jul 13, 2023
1 parent 3e3290d commit 9df01c8
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 38 deletions.
24 changes: 12 additions & 12 deletions packages/react-jss/.size-snapshot.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"react-jss.js": {
"bundled": 126673,
"minified": 46590,
"gzipped": 15618
"bundled": 129153,
"minified": 46606,
"gzipped": 15611
},
"react-jss.min.js": {
"bundled": 93875,
"minified": 36601,
"gzipped": 12819
"bundled": 96355,
"minified": 36617,
"gzipped": 12813
},
"react-jss.cjs.js": {
"bundled": 22967,
"minified": 9723,
"gzipped": 3237
"bundled": 25351,
"minified": 9749,
"gzipped": 3229
},
"react-jss.esm.js": {
"bundled": 20972,
"minified": 8169,
"gzipped": 3014,
"bundled": 23350,
"minified": 8189,
"gzipped": 3010,
"treeshaked": {
"rollup": {
"code": 442,
Expand Down
64 changes: 50 additions & 14 deletions packages/react-jss/src/createUseStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
createStyleSheet,
addDynamicRules,
updateDynamicRules,
removeDynamicRules
removeDynamicRules,
getDynamicRulesClassNames
} from './utils/sheets'
import getSheetIndex from './utils/getSheetIndex'
import {manageSheet, unmanageSheet} from './utils/managers'
Expand All @@ -19,6 +20,16 @@ function getUseInsertionEffect(isSSR) {
useLayoutEffect
}

let fallbackCounter = 0

function useFallbackId() {
return useMemo(() => `:r${++fallbackCounter}:`, [])
}

function getUseId() {
return React.useId || useFallbackId
}

const noTheme = {}

/*
Expand Down Expand Up @@ -75,7 +86,11 @@ const createUseStyles = (styles, options = {}) => {
const context = useContext(JssContext)
const theme = useTheme(data && data.theme)

const [sheet, dynamicRules] = useMemo(() => {
const instanceId = getUseId()().replaceAll(':', '-')

const dynamicRulesRef = useRef(null)

const sheet = useMemo(() => {
const newSheet = createStyleSheet({
context,
styles,
Expand All @@ -95,13 +110,35 @@ const createUseStyles = (styles, options = {}) => {
})
}

return [newSheet, newSheet ? addDynamicRules(newSheet, data) : null]
return newSheet
}, [context, theme])

const dynamicRulesClassNames = useMemo(
() => getDynamicRulesClassNames(sheet, instanceId, context),
[sheet, instanceId, context]
)

getUseInsertionEffect(context.isSSR)(() => {
let dynamicRules = null

if (sheet) {
dynamicRules = addDynamicRules(sheet, data, dynamicRulesClassNames)
}

dynamicRulesRef.current = dynamicRules

return () => {
if (dynamicRules) {
removeDynamicRules(sheet, dynamicRules)
dynamicRulesRef.current = null
}
}
}, [sheet])

getUseInsertionEffect(context.isSSR)(() => {
// We only need to update the rules on a subsequent update and not in the first mount
if (sheet && dynamicRules && !isFirstMount.current) {
updateDynamicRules(data, sheet, dynamicRules)
if (sheet && dynamicRulesRef.current && !isFirstMount.current) {
updateDynamicRules(data, sheet, dynamicRulesRef.current)
}
}, [data])

Expand All @@ -123,18 +160,17 @@ const createUseStyles = (styles, options = {}) => {
sheet,
theme
})

// when sheet changes, remove related dynamic rules
if (dynamicRules) {
removeDynamicRules(sheet, dynamicRules)
}
}
}
}, [sheet]) // TODO: ADD dynamicRules?
}, [sheet])

const classes = useMemo(
() => (sheet && dynamicRules ? getSheetClasses(sheet, dynamicRules) : emptyObject), // TODO: REMOVE dynamicRules FROM CHECK?
[sheet, dynamicRules]
() =>
// TODO: REMOVE dynamicRulesClassNames FROM CHECK?
sheet && dynamicRulesClassNames
? getSheetClasses(sheet, dynamicRulesClassNames)
: emptyObject,
[sheet, dynamicRulesClassNames]
)

useDebugValue(classes)
Expand All @@ -143,7 +179,7 @@ const createUseStyles = (styles, options = {}) => {

useEffect(() => {
isFirstMount.current = false
})
}, [])

return classes
}
Expand Down
2 changes: 2 additions & 0 deletions packages/react-jss/src/flow-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,5 @@ export type ThemedStyles<Theme, Data = {}> = (theme: Theme) => {
}

export type Styles<Theme> = StaticStyles | ThemedStyles<Theme>

export type DynamicRulesClassNames = {[string]: {key: string, id: string}}
15 changes: 11 additions & 4 deletions packages/react-jss/src/utils/getSheetClasses.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {getMeta} from './sheetsMeta'

const getSheetClasses = (sheet, dynamicRules) => {
if (!dynamicRules) {
const getSheetClasses = (sheet, dynamicRulesClassNames) => {
if (!dynamicRulesClassNames) {
return sheet.classes
}

Expand All @@ -12,11 +12,18 @@ const getSheetClasses = (sheet, dynamicRules) => {
}

const classes = {}

for (const key in meta.styles) {
classes[key] = sheet.classes[key]

if (key in dynamicRules) {
classes[key] += ` ${sheet.classes[dynamicRules[key].key]}`
if (key in dynamicRulesClassNames) {
const k = dynamicRulesClassNames[key].key

if (sheet.classes[k]) {
classes[key] += ` ${sheet.classes[k]}`
} else {
classes[key] += ` ${dynamicRulesClassNames[key].id}`
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/react-jss/src/utils/getSheetClasses.js.flow
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
import type {StyleSheet} from 'jss'
import type {DynamicRules, Classes} from '../flow-types'
import type {DynamicRulesClassNames, Classes} from '../flow-types'

type GetSheetClasses = (StyleSheet, DynamicRules | void) => Classes
type GetSheetClasses = (StyleSheet, DynamicRulesClassNames | void) => Classes

declare export default GetSheetClasses
28 changes: 24 additions & 4 deletions packages/react-jss/src/utils/sheets.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,10 @@ export const updateDynamicRules = (data, sheet, rules) => {
}
}

// FIXME: DURING STRICT MODE DOUBLE RENDER THIS FUNCTION IS ADDING DYNAMIC RULES THAT ALREADY EXIST AND IS ALSO RETURNING AN OBJECT WITH THE RULES ADDED DURING SECOND RENDER, SO RULES ADDED DURING FIRST RENDER ARE NEVER REMOVED
export const addDynamicRules = (sheet, data) => {
export const addDynamicRules = (sheet, data, classNames) => {
const meta = getMeta(sheet)

if (!meta) {
if (!meta || !classNames) {
return undefined
}

Expand All @@ -104,7 +103,7 @@ export const addDynamicRules = (sheet, data) => {
// Loop over each dynamic rule and add it to the stylesheet
for (const key in meta.dynamicStyles) {
const initialRuleCount = sheet.rules.index.length
const originalRule = sheet.addRule(key, meta.dynamicStyles[key])
const originalRule = sheet.addRule(classNames[key].key, meta.dynamicStyles[key])

// Loop through all created rules, fixes updating dynamic rules
for (let i = initialRuleCount; i < sheet.rules.index.length; i++) {
Expand All @@ -119,3 +118,24 @@ export const addDynamicRules = (sheet, data) => {

return rules
}

export const getDynamicRulesClassNames = (sheet, instanceId, context) => {
const meta = getMeta(sheet)

if (!meta) {
return undefined
}

const classNames = {}

for (const key in meta.dynamicStyles) {
const k = `${key}-d${instanceId}i`

classNames[key] = {
key: k,
id: (sheet.options.generateId || context.generateId)({key: k}, sheet)
}
}

return classNames
}
14 changes: 12 additions & 2 deletions packages/react-jss/src/utils/sheets.js.flow
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow
import {type StyleSheetFactoryOptions} from 'jss'
import type {StyleSheet} from 'jss'
import type {Context, DynamicRules, Styles} from '../flow-types'
import type {Context, DynamicRules, Styles, DynamicRulesClassNames} from '../flow-types'

type Options<Theme> = {
context: Context,
Expand All @@ -18,4 +18,14 @@ declare export function removeDynamicRules(sheet: StyleSheet, rules: DynamicRule

declare export function updateDynamicRules(data: any, sheet: StyleSheet, rules: DynamicRules): void

declare export function addDynamicRules(sheet: StyleSheet, data: any): DynamicRules | void
declare export function addDynamicRules(
sheet: StyleSheet,
data: any,
classNames: DynamicRulesClassNames
): DynamicRules | void

declare export function getDynamicRulesClassNames(
sheet: StyleSheet,
instanceId: string,
context: Context
): DynamicRulesClassNames | void

0 comments on commit 9df01c8

Please sign in to comment.