Skip to content

Commit e766841

Browse files
authored
Fix React 19 deprecation warning when accessing child ref pre-19 way (#1416)
[#1343] Fix React 19 deprecation warning when accessing child ref pre-19 way `child.ref` is deprecated, and generates a warning in the console in R19 because refs are now first-class props. Check the React version at runtime and if `>=19`, access it via the props; otherwise, keep accessing it as before (a property of the child React element object).
1 parent ffae03a commit e766841

5 files changed

Lines changed: 67 additions & 47 deletions

File tree

.changeset/orange-bananas-laugh.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'focus-trap-react': patch
3+
---
4+
5+
Fix deprecation warning in React 19 when accessing ref the pre-v19 way

demo/js/demo-defaults.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class DemoDefaults extends React.Component {
1414

1515
this.mountTrap = this.mountTrap.bind(this);
1616
this.unmountTrap = this.unmountTrap.bind(this);
17+
18+
this.containerEl = null;
1719
}
1820

1921
mountTrap() {
@@ -24,14 +26,24 @@ class DemoDefaults extends React.Component {
2426
this.setState({ activeTrap: false });
2527
}
2628

29+
// purposely using a ref here to test new React 19 "ref as a prop" behavior
30+
// with fallback to React 18 deprecated behavior
31+
handleCallbackRef(el) {
32+
this.containerEl = el;
33+
this.containerEl?.classList.add('is-active');
34+
}
35+
2736
render() {
2837
const trap = this.state.activeTrap && (
2938
<FocusTrap
3039
focusTrapOptions={{
3140
onDeactivate: this.unmountTrap,
3241
}}
3342
>
34-
<div className="trap is-active">
43+
<div
44+
className="trap is-active"
45+
ref={(el) => this.handleCallbackRef(el)}
46+
>
3547
<p>
3648
Here is a focus trap <a href="#">with</a> <a href="#">some</a>{' '}
3749
<a href="#">focusable</a> parts.

package-lock.json

Lines changed: 24 additions & 38 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@
7070
"@testing-library/react": "^16.1.0",
7171
"@testing-library/user-event": "^14.5.2",
7272
"@types/jquery": "^3.5.32",
73-
"@types/react": "^18.3.1",
74-
"@types/react-dom": "^18.3.0",
73+
"@types/react": "^19.0.0",
74+
"@types/react-dom": "^19.0.0",
7575
"all-contributors-cli": "^6.26.1",
7676
"babel-jest": "^29.7.0",
7777
"babelify": "^10.0.0",
@@ -89,8 +89,8 @@
8989
"jest-watch-typeahead": "^2.2.2",
9090
"onchange": "^7.1.0",
9191
"prettier": "^3.4.2",
92-
"react": "^18.3.1",
93-
"react-dom": "^18.3.1",
92+
"react": "^19.0.0",
93+
"react-dom": "^19.0.0",
9494
"regenerator-runtime": "^0.14.1",
9595
"start-server-and-test": "^2.0.9",
9696
"typescript": "^5.7.2"

src/focus-trap-react.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ const React = require('react');
22
const { createFocusTrap } = require('focus-trap');
33
const { isFocusable } = require('tabbable');
44

5+
/**
6+
* The major version of React currently running.
7+
* @type {number}
8+
*/
9+
const reactVerMajor = parseInt(/^(\d+)\./.exec(React.version)?.[1] ?? 0, 10);
10+
511
/**
612
* @type {import('../index.d.ts').FocusTrap}
713
*/
@@ -391,10 +397,21 @@ class FocusTrap extends React.Component {
391397
const { containerElements } = this.props;
392398

393399
if (child) {
394-
if (typeof child.ref === 'function') {
395-
child.ref(element);
396-
} else if (child.ref) {
397-
child.ref.current = element;
400+
// React 19 moved the `ref` to an official prop
401+
if (reactVerMajor >= 19) {
402+
if (typeof child.props.ref === 'function') {
403+
child.props.ref(element);
404+
} else if (child.props.ref) {
405+
child.props.ref.current = element;
406+
}
407+
} else {
408+
// older versions of React had the `ref` separate from props (still works in R19
409+
// but results in a deprecation warning in Dev builds)
410+
if (typeof child.ref === 'function') {
411+
child.ref(element);
412+
} else if (child.ref) {
413+
child.ref.current = element;
414+
}
398415
}
399416
}
400417

0 commit comments

Comments
 (0)