-
-
Notifications
You must be signed in to change notification settings - Fork 136
Description
Summary
Event handlers defined via cesiumEventProps (such as onMoveEnd, onMoveStart, onChange) do not work correctly in React Strict Mode. After the Strict Mode remount cycle, event listeners are not re-registered, causing events to silently fail.
Environment
- resium: 1.19.2
- React: 19.x (with Strict Mode enabled)
- Tested on: macOS, Chrome
Reproduction Steps
- Create a React app with Strict Mode enabled (default in Create React App / Vite)
- Add a Camera component with an event handler:
import { Viewer, Camera } from 'resium';
function App() {
return (
<Viewer>
<Camera onMoveEnd={() => console.log('Camera moved!')} />
</Viewer>
);
}- Move the camera by dragging the map
- Expected: "Camera moved!" appears in the console
- Actual: Nothing appears in the console
Root Cause Analysis
React Strict Mode Behavior
In development, React Strict Mode intentionally double-invokes lifecycle methods:
mount β unmount β mount (second time)
The Bug in hooks.ts
In the unmount function (around line 270), attachedEvents.current is cleared but prevProps.current is not:
// Detach all events
if (element.current && !isDestroyed(element.current)) {
const attachedEventKeys = Object.keys(attachedEvents.current);
for (const k of attachedEventKeys) {
const eventHandler = element.current[k];
eventHandler?.removeEventListener?.(attachedEvents.current[k]);
}
}
attachedEvents.current = {}; // β Cleared
// prevProps.current is NOT cleared // β BUG
provided.current = undefined;
stateRef.current = undefined;
element.current = undefined;Why This Causes the Bug
The updateProperties function calculates prop differences by comparing props with prevProps.current:
const propDiff = propsKeys
.concat(
Object.keys(prevProps.current).filter(k => !propsKeys.includes(k)),
)
.filter(k => prevProps.current[k] !== props[k]) // Same reference = no diff
.map(k => [k, prevProps.current[k], props[k]]);When useCallback is used (or a stable function reference), prevProps.current['onMoveEnd'] === props['onMoveEnd'] evaluates to true, so no difference is detected and the event listener is never re-added.
Execution Flow
| Step | prevProps.current | attachedEvents.current | Result |
|---|---|---|---|
| 1st mount | {} |
{} |
propDiff=['onMoveEnd'] β Listener added β |
| unmount | {onMoveEnd: fn} (kept) |
{} (cleared) |
Listener removed |
| 2nd mount | {onMoveEnd: fn} (stale) |
{} |
propDiff=[] (no diff) β Listener NOT added β |
Proposed Fix
Add one line to clear prevProps.current in the unmount function:
// hooks.ts, in the unmount function (around line 267-270)
attachedEvents.current = {};
// Fix: Clear prevProps on unmount to ensure proper event re-registration
// when component remounts (e.g., in React Strict Mode)
prevProps.current = {} as Props; // β ADD THIS LINE
provided.current = undefined;
stateRef.current = undefined;
element.current = undefined;After Fix
| Step | prevProps.current | attachedEvents.current | Result |
|---|---|---|---|
| 1st mount | {} |
{} |
propDiff=['onMoveEnd'] β Listener added β |
| unmount | {} (cleared) |
{} (cleared) |
Listener removed |
| 2nd mount | {} |
{} |
propDiff=['onMoveEnd'] β Listener added β |