From 5f32dcae6e6f498923eaa5634cb281c033ea5904 Mon Sep 17 00:00:00 2001 From: Xiaoji Chen Date: Sat, 2 Jan 2021 18:46:42 -0800 Subject: [PATCH] Fix React error when transition is triggered (#1277) --- src/components/interactive-map.js | 54 ++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/components/interactive-map.js b/src/components/interactive-map.js index eddf37a5..d959fc9d 100644 --- a/src/components/interactive-map.js +++ b/src/components/interactive-map.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import {useState, useRef, useMemo, useEffect, forwardRef} from 'react'; +import {useState, useRef, useMemo, useEffect, useLayoutEffect, forwardRef} from 'react'; import * as PropTypes from 'prop-types'; import StaticMap from './static-map'; @@ -266,6 +266,7 @@ function onPointerClick(event) { } /* End of event handers */ +/* eslint-disable max-statements */ const InteractiveMap = forwardRef((props, ref) => { const controller = useMemo(() => props.controller || new MapController(), []); const eventManager = useMemo(() => new EventManager(null, {touchAction: props.touchAction}), []); @@ -294,7 +295,16 @@ const InteractiveMap = forwardRef((props, ref) => { eventCanvasRef.current.style.cursor = props.getCursor(thisRef.state); }; + let inRender = true; + let updateRequested; + const handleViewportChange = (viewState, interactionState, oldViewState) => { + if (inRender) { + // Do not call the callbacks during render - may result in "cannot update during an existing state transition" error. + // Defer the update until after render + updateRequested = [viewState, interactionState, oldViewState]; + return; + } const {onViewStateChange, onViewportChange} = props; if (onViewStateChange) { @@ -362,6 +372,13 @@ const InteractiveMap = forwardRef((props, ref) => { }; }, []); + useLayoutEffect(() => { + if (updateRequested) { + // Perform deferred updates + handleInteractionStateChange(...updateRequested); + } + }); + updateControllerOpts(); const {width, height, style, getCursor} = props; @@ -377,20 +394,27 @@ const InteractiveMap = forwardRef((props, ref) => { [style, width, height, getCursor, thisRef.state] ); - return ( - -
- -
-
- ); + if (!updateRequested) { + // Only rerender if no viewport update has been requested during render. + // Otherwise return the last rendered child, and invoke the callback when we're done. + thisRef._child = ( + +
+ +
+
+ ); + } + + inRender = false; + return thisRef._child; }); InteractiveMap.supported = StaticMap.supported;