fix(P4A): guard ImageAnnotator load callbacks with cancelled flag (React strict double-mount)
This commit is contained in:
@@ -51,18 +51,31 @@ export function ImageAnnotator({ file, onCancel, onSave }: Props) {
|
|||||||
useEffect(() => { onCancelRef.current = onCancel; }, [onCancel]);
|
useEffect(() => { onCancelRef.current = onCancel; }, [onCancel]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// React 18 strict mode in dev double-mounts effects to test idempotency.
|
||||||
|
// The first run creates a blob URL, sets img.src, returns a cleanup
|
||||||
|
// that revokes — and the cleanup fires BEFORE the (still-in-flight)
|
||||||
|
// image fetch completes. The browser then emits ERR_FILE_NOT_FOUND for
|
||||||
|
// the revoked URL → img.onerror → modal closes instantly. The
|
||||||
|
// `cancelled` flag guards every callback so a torn-down run can't
|
||||||
|
// close the modal that the second mount just opened.
|
||||||
|
let cancelled = false;
|
||||||
const url = URL.createObjectURL(file);
|
const url = URL.createObjectURL(file);
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
|
if (cancelled) return;
|
||||||
imageRef.current = img;
|
imageRef.current = img;
|
||||||
setImageLoaded(true);
|
setImageLoaded(true);
|
||||||
};
|
};
|
||||||
img.onerror = () => {
|
img.onerror = () => {
|
||||||
|
if (cancelled) return;
|
||||||
console.error('ImageAnnotator: failed to decode source image');
|
console.error('ImageAnnotator: failed to decode source image');
|
||||||
onCancelRef.current();
|
onCancelRef.current();
|
||||||
};
|
};
|
||||||
img.src = url;
|
img.src = url;
|
||||||
return () => URL.revokeObjectURL(url);
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
}, [file]);
|
}, [file]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user