Skip to content

Commit 061d403

Browse files
committed
fix: add comprehensive React shim to fix useLayoutEffect and createContext errors
- Added react-shim.ts that polyfills all required React global methods - Added import statements to all Radix UI component files - Updated index.html to include more robust React polyfill with non-configurable properties - Improved TypeScript type handling for global React object
1 parent fb96014 commit 061d403

File tree

9 files changed

+83
-19
lines changed

9 files changed

+83
-19
lines changed

index.html

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,33 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Beacons</title>
7-
<!-- Preload polyfill to ensure React is available globally -->
7+
<!-- CRITICAL: Polyfill to ensure React is available globally -->
88
<script>
9-
// Global React polyfill to prevent errors like:
10-
// - Cannot read properties of undefined (reading 'useLayoutEffect')
11-
// - Cannot read properties of undefined (reading 'createContext')
12-
if (typeof window !== 'undefined') {
13-
window.React = window.React || {};
9+
// THIS MUST RUN BEFORE ANY OTHER SCRIPTS
10+
(function() {
11+
// Global React polyfill to prevent errors like:
12+
// - Cannot read properties of undefined (reading 'useLayoutEffect')
13+
// - Cannot read properties of undefined (reading 'createContext')
14+
window.React = {};
1415
window.React.useState = function() { return [undefined, function() {}]; };
1516
window.React.useEffect = function() { return function() {}; };
1617
window.React.useLayoutEffect = function() { return function() {}; };
1718
window.React.useRef = function() { return { current: null }; };
1819
window.React.useContext = function() { return {}; };
19-
window.React.createContext = function() { return { Provider: function() {}, Consumer: function() {} }; };
20+
window.React.createContext = function() { return { Provider: function(){}, Consumer: function(){} }; };
2021
window.React.forwardRef = function(fn) { return fn; };
21-
}
22+
window.React.Children = { map: function(){}, forEach: function(){}, count: function(){}, only: function(){} };
23+
window.React.Fragment = "Fragment";
24+
window.React.StrictMode = "StrictMode";
25+
window.React.cloneElement = function() { return {}; };
26+
window.React.createElement = function() { return {}; };
27+
28+
// Ensure this polyfill isn't overridden
29+
Object.defineProperty(window, 'React', {
30+
configurable: false,
31+
writable: false
32+
});
33+
})();
2234
</script>
2335
</head>
2436
<body>

src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
'use client';
22

3+
// Import reactive shim first to ensure global React is available
4+
import './lib/react-shim';
5+
36
import React, { useEffect } from 'react';
47
import { TooltipProvider } from '@radix-ui/react-tooltip';
58

src/components/ui/dialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
// Import the polyfill early
4-
import '../../lib/utils/radix-utils';
3+
// Import react-shim to ensure React global methods exist
4+
import '../../lib/react-shim';
55

66
import * as React from 'react';
77
import * as DialogPrimitive from '@radix-ui/react-dialog';

src/components/ui/dropdown-menu.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
// Import the polyfill early
4-
import '../../lib/utils/radix-utils';
3+
// Import react-shim to ensure React global methods exist
4+
import '../../lib/react-shim';
55

66
import * as React from 'react';
77
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';

src/components/ui/label.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
// Import the polyfill early
4-
import '../../lib/utils/radix-utils';
3+
// Import react-shim to ensure React global methods exist
4+
import '../../lib/react-shim';
55

66
import * as React from 'react';
77
import * as LabelPrimitive from '@radix-ui/react-label';

src/components/ui/popover.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
// Import the polyfill early
4-
import '../../lib/utils/radix-utils';
3+
// Import react-shim to ensure React global methods exist
4+
import '../../lib/react-shim';
55

66
import * as React from 'react';
77
import * as PopoverPrimitive from '@radix-ui/react-popover';

src/components/ui/tooltip.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
// Import the polyfill early
4-
import '../../lib/utils/radix-utils';
3+
// Import react-shim to ensure React global methods exist
4+
import '../../lib/react-shim';
55

66
import * as React from 'react';
77
import * as TooltipPrimitive from '@radix-ui/react-tooltip';

src/lib/react-shim.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* This module ensures React global methods are available
3+
* It's particularly needed for Radix components that expect
4+
* React.useLayoutEffect and React.createContext to be available globally
5+
*/
6+
7+
// Force window.React to exist and have key methods
8+
if (typeof window !== 'undefined') {
9+
// Create stubs for common React methods
10+
const noop = () => {};
11+
const noopReturn = () => () => {};
12+
const emptyObj = {};
13+
14+
// Only add if missing
15+
if (!window.React) {
16+
(window as any).React = {};
17+
}
18+
19+
// Add missing methods - use type assertion for TypeScript
20+
const reactShim = (window as any).React;
21+
22+
if (!reactShim.useState) reactShim.useState = () => [undefined, noop];
23+
if (!reactShim.useEffect) reactShim.useEffect = noopReturn;
24+
if (!reactShim.useLayoutEffect) reactShim.useLayoutEffect = reactShim.useEffect || noopReturn;
25+
if (!reactShim.useRef) reactShim.useRef = () => ({ current: null });
26+
if (!reactShim.useContext) reactShim.useContext = () => emptyObj;
27+
if (!reactShim.createContext) reactShim.createContext = () => ({ Provider: noop, Consumer: noop });
28+
if (!reactShim.forwardRef) reactShim.forwardRef = (fn: any) => fn;
29+
if (!reactShim.createElement) reactShim.createElement = () => emptyObj;
30+
if (!reactShim.cloneElement) reactShim.cloneElement = () => emptyObj;
31+
32+
// Only add these if they don't exist
33+
if (!reactShim.Children) {
34+
reactShim.Children = {
35+
map: noop,
36+
forEach: noop,
37+
count: noop,
38+
only: noop,
39+
};
40+
}
41+
42+
if (!reactShim.Fragment) reactShim.Fragment = 'Fragment';
43+
if (!reactShim.StrictMode) reactShim.StrictMode = 'StrictMode';
44+
}
45+
46+
// Export to ensure this file isn't tree-shaken
47+
export default function ensureReactExists() {
48+
return true;
49+
}

src/main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Import the polyfill first, before any other imports
2-
import './lib/utils/radix-utils';
2+
import './lib/react-shim';
33

44
import { StrictMode } from 'react';
55
import { createRoot } from 'react-dom/client';

0 commit comments

Comments
 (0)