Skip to content

Commit fafbcb1

Browse files
feat(reconciler): add React 19.2.X support (#3224)
1 parent 2eba70b commit fafbcb1

File tree

7 files changed

+152
-6
lines changed

7 files changed

+152
-6
lines changed

.changeset/perfect-feet-turn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-pdf/reconciler": major
3+
---
4+
5+
feat: add React 19.2.X support

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@
6969
"react-16": "npm:react@^16.8.0",
7070
"react-17": "npm:react@^17.0.0",
7171
"react-19": "npm:react@19.0.0-rc-66855b96-20241106",
72+
"react-19-2": "npm:react@^19.2.0",
7273
"react-dom": "^18.2.0",
7374
"react-dom-16": "npm:react-dom@^16.8.0",
7475
"react-dom-17": "npm:react-dom@^17.0.0",
7576
"react-dom-19": "npm:react-dom@19.0.0-rc-66855b96-20241106",
77+
"react-dom-19-2": "npm:react-dom@^19.2.0",
7678
"rimraf": "^2.6.3",
7779
"rollup": "^4.34.6",
7880
"rollup-plugin-copy": "^3.5.0",

packages/reconciler/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@react-pdf/reconciler",
3-
"version": "1.1.4",
3+
"version": "1.2.0",
44
"license": "MIT",
55
"description": "Define uninitialized elements",
66
"author": "Diego Muracciole <diegomuracciole@gmail.com>",
@@ -34,6 +34,7 @@
3434
"ast-types": "^0.14.2",
3535
"react-reconciler-23": "npm:react-reconciler@0.23.0",
3636
"react-reconciler-31": "npm:react-reconciler@0.31.0-rc-603e6108-20241029",
37+
"react-reconciler-33": "npm:react-reconciler@0.33.0",
3738
"recast": "^0.23.9"
3839
}
3940
}

packages/reconciler/rollup.config.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ export default [
1111
{
1212
input: 'src/index.ts',
1313
output: { format: 'es', file: 'lib/index.js' },
14-
external: ['./reconciler-23.js', './reconciler-31.js'],
14+
external: [
15+
'./reconciler-23.js',
16+
'./reconciler-31.js',
17+
'./reconciler-33.js',
18+
],
1519
plugins: [typescript()],
1620
},
1721
{
@@ -36,6 +40,17 @@ export default [
3640
terser({ compress: { dead_code: true } }),
3741
],
3842
},
43+
{
44+
input: 'src/reconciler-33.ts',
45+
output: { format: 'es', file: 'lib/reconciler-33.js' },
46+
plugins: [
47+
typescript(),
48+
resolve({ resolveOnly: ['react-reconciler-33'] }),
49+
commonjs({ esmExternals: (id) => id === 'scheduler' }),
50+
trimReconciler(),
51+
terser({ compress: { dead_code: true } }),
52+
],
53+
},
3954
{
4055
input: './lib/types/index.d.ts',
4156
output: [{ file: 'lib/index.d.ts', format: 'es' }],

packages/reconciler/src/index.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import React from 'react';
22
import createRendererForReact19 from './reconciler-31.js';
3+
import createRendererForReact19_2 from './reconciler-33.js';
34
import createRendererForReact18AndLess from './reconciler-23.js';
45

5-
const isReact19 = React.version.startsWith('19');
6+
const [major, minor] = React.version.split('.').map((v) => parseInt(v, 10));
67

7-
export default isReact19
8-
? createRendererForReact19
9-
: createRendererForReact18AndLess;
8+
let renderer;
9+
10+
if (major >= 20 || (major === 19 && minor >= 2)) {
11+
// Use the reconciler built for 19.2, assuming it's compatible with later versions.
12+
renderer = createRendererForReact19_2;
13+
} else if (major === 19) {
14+
// Use the older 19.x reconciler (for 19.0.x and 19.1.x)
15+
renderer = createRendererForReact19;
16+
} else {
17+
// Fallback for React 18.x and previous versions.
18+
renderer = createRendererForReact18AndLess;
19+
}
20+
21+
export default renderer;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import Reconciler from 'react-reconciler-33';
2+
import {
3+
ConcurrentRoot,
4+
DefaultEventPriority,
5+
} from 'react-reconciler-33/constants';
6+
7+
import propsEqual from './propsEqual';
8+
import { ReconcilerFactory } from './types';
9+
10+
const emptyObject = {};
11+
12+
const logRecoverableError = console.error;
13+
14+
const createRenderer: ReconcilerFactory = ({
15+
appendChild,
16+
appendChildToContainer,
17+
commitTextUpdate,
18+
commitUpdate,
19+
createInstance,
20+
createTextInstance,
21+
insertBefore,
22+
removeChild,
23+
removeChildFromContainer,
24+
resetAfterCommit,
25+
}) => {
26+
const _commitUpdate = (instance, type, oldProps, newProps) => {
27+
if (propsEqual(oldProps, newProps)) return;
28+
commitUpdate(instance, null, type, oldProps, newProps);
29+
};
30+
31+
const reconciler = Reconciler({
32+
appendChild,
33+
appendChildToContainer,
34+
appendInitialChild: appendChild,
35+
createInstance,
36+
createTextInstance,
37+
insertBefore,
38+
commitUpdate: _commitUpdate,
39+
commitTextUpdate,
40+
removeChild,
41+
removeChildFromContainer,
42+
resetAfterCommit,
43+
noTimeout: -1,
44+
shouldSetTextContent: () => false,
45+
finalizeInitialChildren: () => false,
46+
getPublicInstance: (instance) => instance,
47+
getRootHostContext: () => emptyObject,
48+
getChildHostContext: () => emptyObject,
49+
prepareForCommit() {},
50+
clearContainer() {},
51+
resetTextContent() {},
52+
getCurrentUpdatePriority: () => DefaultEventPriority,
53+
maySuspendCommit: () => false,
54+
requestPostPaintCallback: () => {},
55+
resolveUpdatePriority: () => DefaultEventPriority,
56+
setCurrentUpdatePriority: () => {},
57+
shouldAttemptEagerTransition: () => false,
58+
});
59+
60+
const createContainer = (container) => {
61+
return reconciler.createContainer(
62+
container, // containerInfo: Container
63+
ConcurrentRoot, // tag: RootTag
64+
null, // hydration callbacks: null | SuspenseHydrationCallbacks
65+
false, // isStrictMode: boolean
66+
null, // concurrentUpdatesByDefaultOverride: null | boolean
67+
'', // identifierPrefix: string
68+
logRecoverableError, // onUncaughtError: (error: mixed, errorInfo: {+componentStack?: ?string}) => void
69+
logRecoverableError, // onCaughtError: (error: mixed, errorInfo: { ... }) => void
70+
logRecoverableError, // onRecoverableError: (error: mixed, errorInfo: {+componentStack?: ?string}) => void
71+
() => {}, // NEW IN REACT 19.2.0: onDefaultTransitionIndicator: () => void | (() => void)
72+
null, // transitionCallbacks: null | TransitionTracingCallbacks
73+
);
74+
};
75+
76+
const updateContainer = (doc, mountNode, parentComponent, callback) => {
77+
reconciler.updateContainerSync(doc, mountNode, parentComponent, callback);
78+
reconciler.flushSyncWork();
79+
};
80+
81+
return {
82+
createContainer,
83+
updateContainer,
84+
};
85+
};
86+
87+
export default createRenderer;

yarn.lock

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8959,6 +8959,11 @@ randombytes@^2.1.0:
89598959
loose-envify "^1.1.0"
89608960
object-assign "^4.1.1"
89618961

8962+
"react-19-2@npm:react@^19.2.0":
8963+
version "19.2.0"
8964+
resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5"
8965+
integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==
8966+
89628967
"react-19@npm:react@19.0.0-rc-66855b96-20241106", react@19.0.0-rc-66855b96-20241106:
89638968
name react-19
89648969
version "19.0.0-rc-66855b96-20241106"
@@ -8984,6 +8989,13 @@ randombytes@^2.1.0:
89848989
object-assign "^4.1.1"
89858990
scheduler "^0.20.2"
89868991

8992+
"react-dom-19-2@npm:react-dom@^19.2.0":
8993+
version "19.2.0"
8994+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8"
8995+
integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==
8996+
dependencies:
8997+
scheduler "^0.27.0"
8998+
89878999
"react-dom-19@npm:react-dom@19.0.0-rc-66855b96-20241106", react-dom@19.0.0-rc-66855b96-20241106:
89889000
name react-dom-19
89899001
version "19.0.0-rc-66855b96-20241106"
@@ -9032,6 +9044,13 @@ react-is@^18.0.0:
90329044
dependencies:
90339045
scheduler "0.25.0-rc-603e6108-20241029"
90349046

9047+
"react-reconciler-33@npm:react-reconciler@0.33.0":
9048+
version "0.33.0"
9049+
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.33.0.tgz#9dd20208d45baa5b0b4701781f858236657f15e1"
9050+
integrity sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==
9051+
dependencies:
9052+
scheduler "^0.27.0"
9053+
90359054
react-refresh@^0.14.0:
90369055
version "0.14.0"
90379056
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
@@ -9582,6 +9601,11 @@ scheduler@^0.23.2:
95829601
dependencies:
95839602
loose-envify "^1.1.0"
95849603

9604+
scheduler@^0.27.0:
9605+
version "0.27.0"
9606+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd"
9607+
integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==
9608+
95859609
schema-utils@^3.1.1, schema-utils@^3.2.0:
95869610
version "3.3.0"
95879611
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe"

0 commit comments

Comments
 (0)