mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 03:00:58 +08:00
fix(react): add useRef wrapper to useIonOverlay state to avoid stale references (#24553)
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { ReactComponentOrElement } from '../models';
|
||||
@ -25,12 +25,29 @@ export const IonOverlayManager: React.FC<IonOverlayManagerProps> = ({
|
||||
onAddOverlay,
|
||||
onRemoveOverlay,
|
||||
}) => {
|
||||
const [overlays, setOverlays] = useState<{
|
||||
type OverlaysList = {
|
||||
[key: string]: {
|
||||
component: any;
|
||||
containerElement: HTMLDivElement;
|
||||
};
|
||||
}>({});
|
||||
};
|
||||
|
||||
/**
|
||||
* Because of the way we're passing around the addOverlay and removeOverlay
|
||||
* callbacks, by the time they finally get called, they use a stale reference
|
||||
* to the state that only has the initial values. So if two overlays are opened
|
||||
* at the same time, both using useIonModal or similar (such as through nesting),
|
||||
* the second will erase the first from the overlays list. This causes the content
|
||||
* of the first overlay to unmount.
|
||||
*
|
||||
* We wrap the state in useRef to ensure the two callbacks always use the most
|
||||
* up-to-date version.
|
||||
*
|
||||
* Further reading: https://stackoverflow.com/a/56554056
|
||||
*/
|
||||
const [overlays, setOverlays] = useState<OverlaysList>({});
|
||||
const overlaysRef = useRef<OverlaysList>({});
|
||||
overlaysRef.current = overlays;
|
||||
|
||||
useEffect(() => {
|
||||
/* Setup the callbacks that get called from <IonApp /> */
|
||||
@ -43,13 +60,13 @@ export const IonOverlayManager: React.FC<IonOverlayManagerProps> = ({
|
||||
component: ReactComponentOrElement,
|
||||
containerElement: HTMLDivElement
|
||||
) => {
|
||||
const newOverlays = { ...overlays };
|
||||
const newOverlays = { ...overlaysRef.current };
|
||||
newOverlays[id] = { component, containerElement };
|
||||
setOverlays(newOverlays);
|
||||
};
|
||||
|
||||
const removeOverlay = (id: string) => {
|
||||
const newOverlays = { ...overlays };
|
||||
const newOverlays = { ...overlaysRef.current };
|
||||
delete newOverlays[id];
|
||||
setOverlays(newOverlays);
|
||||
};
|
||||
|
Reference in New Issue
Block a user