Skip to content

Commit b184854

Browse files
committed
fix: improve React polyfill to safely check and set properties
- Fixed 'Cannot assign to read only property React of object #<Window>' error - Removed problematic polyfill from index.html that was causing conflicts - Improved the React shim to safely check properties before setting them - Added better error handling to quietly handle read-only property issues
1 parent 8fc4fb3 commit b184854

File tree

3 files changed

+91
-68
lines changed

3 files changed

+91
-68
lines changed

index.html

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,6 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Beacons</title>
7-
<!-- CRITICAL: Polyfill to ensure React is available globally -->
8-
<script>
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 = {};
15-
window.React.useState = function() { return [undefined, function() {}]; };
16-
window.React.useEffect = function() { return function() {}; };
17-
window.React.useLayoutEffect = function() { return function() {}; };
18-
window.React.useRef = function() { return { current: null }; };
19-
window.React.useContext = function() { return {}; };
20-
window.React.createContext = function() { return { Provider: function(){}, Consumer: function(){} }; };
21-
window.React.forwardRef = function(fn) { return fn; };
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-
})();
34-
</script>
357
</head>
368
<body>
379
<div id="root"></div>

src/lib/react-shim.ts

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,62 @@
44
* React.useLayoutEffect and React.createContext to be available globally
55
*/
66

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 = {};
7+
// Safely check and set properties
8+
function safelySetProperty(obj: any, prop: string, value: any) {
9+
// Skip if property already exists
10+
if (obj[prop] !== undefined) return;
1311

14-
// Only add if missing
15-
if (!window.React) {
16-
(window as any).React = {};
12+
try {
13+
// Try to set the property - this will fail if the property is read-only
14+
obj[prop] = value;
15+
} catch (e) {
16+
// Property is read-only or can't be assigned - just log in dev mode
17+
if (process.env.NODE_ENV === 'development') {
18+
console.debug(`Unable to set ${prop} on React`, e);
19+
}
1720
}
21+
}
22+
23+
// Create stubs for common React methods
24+
const noop = () => {};
25+
const noopReturn = () => () => {};
26+
const emptyObj = {};
27+
28+
// Apply React polyfills safely
29+
if (typeof window !== 'undefined') {
30+
const windowAny = window as any;
1831

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-
};
32+
// We won't try to create window.React if it doesn't exist
33+
// This avoids the error of trying to create a property on window
34+
if (windowAny.React) {
35+
// Only try to set properties that don't already exist
36+
safelySetProperty(windowAny.React, 'useState', () => [undefined, noop]);
37+
safelySetProperty(windowAny.React, 'useEffect', noopReturn);
38+
safelySetProperty(windowAny.React, 'useLayoutEffect', windowAny.React.useEffect || noopReturn);
39+
safelySetProperty(windowAny.React, 'useRef', () => ({ current: null }));
40+
safelySetProperty(windowAny.React, 'useContext', () => emptyObj);
41+
safelySetProperty(windowAny.React, 'createContext', () => ({ Provider: noop, Consumer: noop }));
42+
safelySetProperty(windowAny.React, 'forwardRef', (fn: any) => fn);
43+
safelySetProperty(windowAny.React, 'createElement', () => emptyObj);
44+
safelySetProperty(windowAny.React, 'cloneElement', () => emptyObj);
45+
46+
// Add Children if it doesn't exist
47+
if (!windowAny.React.Children) {
48+
try {
49+
windowAny.React.Children = {
50+
map: noop,
51+
forEach: noop,
52+
count: noop,
53+
only: noop,
54+
};
55+
} catch (e) {
56+
// Couldn't set Children
57+
}
58+
}
59+
60+
safelySetProperty(windowAny.React, 'Fragment', 'Fragment');
61+
safelySetProperty(windowAny.React, 'StrictMode', 'StrictMode');
4062
}
41-
42-
if (!reactShim.Fragment) reactShim.Fragment = 'Fragment';
43-
if (!reactShim.StrictMode) reactShim.StrictMode = 'StrictMode';
4463
}
4564

4665
// Export to ensure this file isn't tree-shaken

src/lib/utils/radix-utils.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,46 @@
44
* in certain environments like SSR or when window.React isn't properly set
55
*/
66

7-
// Apply React useLayoutEffect polyfill
7+
// Safely check and set properties
8+
function safelySetProperty(obj: any, prop: string, value: any) {
9+
// Skip if property already exists
10+
if (obj[prop] !== undefined) return;
11+
12+
try {
13+
// Try to set the property - this will fail if the property is read-only
14+
obj[prop] = value;
15+
} catch (e) {
16+
// Property is read-only or can't be assigned - just log in dev mode
17+
if (process.env.NODE_ENV === 'development') {
18+
console.debug(`Unable to set ${prop}`, e);
19+
}
20+
}
21+
}
22+
23+
// Apply React polyfills safely
824
if (typeof window !== 'undefined') {
9-
// We're in the browser, so we can safely create a React object on window
10-
// This ensures that libraries that expect to find React on window won't break
11-
// @ts-ignore - intentionally modifying window
12-
window.React = window.React || {};
13-
// @ts-ignore - intentionally modifying window.React
14-
window.React.useLayoutEffect = window.React.useLayoutEffect || window.React.useEffect || function() { return function() {}; };
25+
// Check if window.React exists
26+
const windowAny = window as any;
27+
28+
// Create React object if it doesn't exist
29+
if (!windowAny.React) {
30+
try {
31+
windowAny.React = {};
32+
} catch (e) {
33+
// If we can't assign to window.React, there's not much we can do
34+
console.debug('Unable to create window.React', e);
35+
}
36+
}
37+
38+
// Only try to set properties if window.React exists
39+
if (windowAny.React) {
40+
// Safely set individual properties
41+
safelySetProperty(windowAny.React, 'useLayoutEffect',
42+
windowAny.React.useEffect || function() { return function() {}; });
43+
44+
safelySetProperty(windowAny.React, 'createContext',
45+
function() { return { Provider: function() {}, Consumer: function() {} }; });
46+
}
1547
}
1648

1749
// Export a dummy function to make TypeScript happy when importing this module

0 commit comments

Comments
 (0)