Skip to content

Commit 0aefc4c

Browse files
shlenskyyarastqt
authored andcommitted
feat: upgrade to React 19 with new JSX runtime
- Update react, react-dom to 19.0.0 - Migrate from createElement to jsx from react/jsx-runtime - Remove forwardRef usage in withBemMod (refs forwarded by default in React 19) - Update peerDependencies to require React 19 - Update ESLint config for React 19 - Fix typos in comments and variable names BREAKING CHANGE: drop support for React 16, 17, and 18
1 parent 2794188 commit 0aefc4c

9 files changed

Lines changed: 32 additions & 43 deletions

File tree

.eslintrc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
},
1818
"settings": {
1919
"react": {
20-
"pragma": "React",
21-
"version": "16.0"
20+
"version": "19.0"
2221
}
2322
},
2423
"rules": {
@@ -178,7 +177,7 @@
178177
"react/no-find-dom-node": 2,
179178
"react/no-multi-comp": 0,
180179
"react/no-set-state": 0,
181-
"react/react-in-jsx-scope": 2,
180+
"react/react-in-jsx-scope": 0,
182181
"react/require-optimization": 0,
183182
"react/self-closing-comp": 2,
184183
"react/style-prop-object": 2,

environment.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Eenvironment flag for development.
2+
* Environment flag for development.
33
* @internal
44
*/
55
declare const __DEV__: boolean

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@commitlint/cli": "17.3.0",
1414
"@commitlint/config-conventional": "17.3.0",
1515
"@types/jest": "26.0.14",
16-
"@types/react": "18.0.26",
16+
"@types/react": "19.0.6",
1717
"@typescript-eslint/eslint-plugin": "2.22.0",
1818
"@typescript-eslint/parser": "2.22.0",
1919
"chalk": "2.4.1",
@@ -24,13 +24,13 @@
2424
"husky": "3.0.5",
2525
"jest": "29.3.1",
2626
"jest-environment-jsdom": "29.3.1",
27-
"@testing-library/react": "13.4.0",
27+
"@testing-library/react": "16.1.0",
2828
"lerna": "3.5.1",
2929
"lint-staged": "9.2.5",
3030
"prettier": "2.8.1",
3131
"pretty-bytes": "5.2.0",
32-
"react": "18.2.0",
33-
"react-dom": "18.2.0",
32+
"react": "19.0.0",
33+
"react-dom": "19.0.0",
3434
"rollup": "1.10.1",
3535
"rollup-plugin-node-resolve": "4.2.3",
3636
"rollup-plugin-replace": "2.1.0",

packages/core/core.ts

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { ComponentType, FC, createElement, forwardRef } from 'react'
1+
import { ComponentType, FC, JSX } from 'react'
2+
import { jsx } from 'react/jsx-runtime'
23
import { cn, NoStrictEntityMods, ClassNameFormatter } from '@bem-react/classname'
34
import { classnames } from '@bem-react/classnames'
45

@@ -71,7 +72,7 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
7172
entity = entity || cn(blockName)
7273
entityClassName = entityClassName || entity()
7374

74-
const BemMod = forwardRef((props: T & K, ref) => {
75+
const BemMod: FC<T & K> = (props) => {
7576
modNames = modNames || Object.keys(mod)
7677

7778
// TODO: For performance can rewrite `every` to `for (;;)`.
@@ -82,8 +83,6 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
8283
return modValue === propValue || (modValue === '*' && Boolean(propValue))
8384
})
8485

85-
const nextProps = Object.assign({}, props, { ref })
86-
8786
if (isModifierMatched) {
8887
const modifiers = modNames.reduce((acc: Dictionary, key: string) => {
8988
if (mod[key] !== '*') acc[key] = mod[key]
@@ -92,8 +91,8 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
9291
}, {})
9392
modifierClassName = modifierClassName || entity(modifiers)
9493

95-
nextProps.className = classnames(modifierClassName, props.className)
96-
// Replace first entityClassName for remove duplcates from className.
94+
const className = classnames(modifierClassName, props.className)
95+
// Replace first entityClassName for remove duplicates from className.
9796
.replace(`${entityClassName} `, '')
9897

9998
if (typeof enhance === 'function') {
@@ -111,13 +110,11 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
111110
ModifiedComponent = WrappedComponent as any
112111
}
113112

114-
// Use createElement instead of jsx to avoid __assign from tslib.
115-
return createElement(ModifiedComponent, nextProps)
113+
return jsx(ModifiedComponent, Object.assign({}, props, { className }))
116114
}
117115

118-
// Use createElement instead of jsx to avoid __assign from tslib.
119-
return createElement(WrappedComponent, nextProps)
120-
})
116+
return jsx(WrappedComponent, props)
117+
}
121118

122119
if (__DEV__) {
123120
setDisplayName(BemMod, {
@@ -130,11 +127,8 @@ export function withBemMod<T, U extends IClassNameProps = {}>(
130127
return BemMod
131128
}
132129

133-
// Ignore `forwardRef` typings to keep compatibility with `HOC<T>`
134-
const withMod = (WithBemMod as any) as {
135-
<K extends IClassNameProps = {}>(WrappedComponent: ComponentType<T & K>): (
136-
props: T & K,
137-
) => React.ReactElement
130+
const withMod = WithBemMod as any as {
131+
<K extends IClassNameProps = {}>(WrappedComponent: ComponentType<T & K>): FC<T & K>
138132

139133
__isSimple: boolean
140134
__blockName: string
@@ -194,7 +188,7 @@ function composeSimple(mods: any[]) {
194188
return (Base: ComponentType<any>) => {
195189
function SimpleComposeWrapper(props: Record<string, any>) {
196190
const modifiers: NoStrictEntityMods = {}
197-
const newProps: any = { ...props }
191+
const newProps: any = Object.assign({}, props)
198192

199193
for (let key of modNames) {
200194
const modValues = allMods[key]
@@ -221,7 +215,7 @@ function composeSimple(mods: any[]) {
221215

222216
newProps.className = entity(modifiers, [props.className])
223217

224-
return createElement(Base, newProps)
218+
return jsx(Base, newProps)
225219
}
226220
if (__DEV__) {
227221
const allModsFormatted = Object.keys(allMods)
@@ -320,9 +314,9 @@ export function compose() {
320314
f.__isSimple ? simple.push(f) : enhanced.push(f)
321315
}
322316

323-
const oprimizedFns = simple.length ? [composeSimple(simple), ...enhanced] : enhanced
317+
const optimizedFns = simple.length ? [composeSimple(simple), ...enhanced] : enhanced
324318

325-
return oprimizedFns.reduce(
319+
return optimizedFns.reduce(
326320
(a, b) => {
327321
return function() {
328322
return a(b.apply(0, arguments))

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@bem-react/classnames": "1.4.0"
2020
},
2121
"peerDependencies": {
22-
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
22+
"react": "^19.0.0"
2323
},
2424
"files": ["build", "core.d.ts"],
2525
"main": "./build/core.cjs",

packages/di/di.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import React, {
2-
ReactNode,
3-
FC,
4-
ComponentType,
5-
createContext,
6-
useContext,
7-
useRef,
8-
createElement,
9-
} from 'react'
1+
import { ReactNode, FC, ComponentType, createContext, useContext, useRef } from 'react'
2+
import { jsx } from 'react/jsx-runtime'
103

114
export type RegistryContext = Record<string, Registry>
125

@@ -49,8 +42,7 @@ export function withRegistry() {
4942

5043
return (
5144
<RegistryProvider value={providedRegistriesRef.current}>
52-
{/* Use createElement instead of jsx to avoid __assign from tslib. */}
53-
{createElement(Component, props)}
45+
{jsx(Component, props)}
5446
</RegistryProvider>
5547
)
5648
}}

packages/di/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"unit": "../../node_modules/.bin/jest --config ../../.config/jest/jest.config.js"
1616
},
1717
"peerDependencies": {
18-
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
18+
"react": "^19.0.0"
1919
},
2020
"files": ["build", "di.d.ts"],
2121
"main": "./build/di.cjs",

scripts/rollup/build.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ function getExternalDependencies(packagePath) {
3333
const content = readFileSync(packageJsonPath, 'utf-8')
3434
const { dependencies, peerDependencies } = JSON.parse(content)
3535

36-
return [...Object.keys(Object(dependencies)), ...Object.keys(Object(peerDependencies))]
36+
return [
37+
...Object.keys(Object(dependencies)),
38+
...Object.keys(Object(peerDependencies)),
39+
'react/jsx-runtime',
40+
]
3741
}
3842

3943
function getTypescriptConfig(packagePath) {

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"compilerOptions": {
33
/* Basic Options */
44
"declaration": true,
5-
"jsx": "react",
5+
"jsx": "react-jsx",
66
"lib": ["es6", "dom"],
77
"module": "esnext",
88
"moduleResolution": "node",

0 commit comments

Comments
 (0)