From a78b255fb2e10284ec15aef86ad32aaa434cdc9c Mon Sep 17 00:00:00 2001 From: William Candillon Date: Tue, 7 Jan 2025 22:57:17 +0100 Subject: [PATCH] =?UTF-8?q?fix(=F0=9F=90=9B):=20Fix=20serious=20memory=20e?= =?UTF-8?q?rror=20with=20data=20loading=20hooks=20(useImage,=20useFont)=20?= =?UTF-8?q?=20(#2866)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hooks like useImage wrong manually dispose the data when unmounting even thought the reference might be still used somewhere else. In the case of the Skia reconciler, the image is disposed even before any kind of unmounting is signaled to the reconciler. --- apps/paper/ios/Podfile.lock | 8 +------- .../reanimated/useAnimatedImageValue.ts | 19 ++++--------------- packages/skia/src/skia/core/AnimatedImage.ts | 5 ++--- packages/skia/src/skia/core/Data.ts | 12 +++--------- 4 files changed, 10 insertions(+), 34 deletions(-) diff --git a/apps/paper/ios/Podfile.lock b/apps/paper/ios/Podfile.lock index 77c38d4ecf..8c192df5e4 100644 --- a/apps/paper/ios/Podfile.lock +++ b/apps/paper/ios/Podfile.lock @@ -1694,8 +1694,6 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNSVG (15.9.0): - - React-Core - SocketRocket (0.7.0) - Yoga (0.0.0) @@ -1770,7 +1768,6 @@ DEPENDENCIES: - RNGestureHandler (from `../../../node_modules/react-native-gesture-handler`) - RNReanimated (from `../../../node_modules/react-native-reanimated`) - RNScreens (from `../../../node_modules/react-native-screens`) - - RNSVG (from `../node_modules/react-native-svg`) - Yoga (from `../../../node_modules/react-native/ReactCommon/yoga`) SPEC REPOS: @@ -1915,8 +1912,6 @@ EXTERNAL SOURCES: :path: "../../../node_modules/react-native-reanimated" RNScreens: :path: "../../../node_modules/react-native-screens" - RNSVG: - :path: "../node_modules/react-native-svg" Yoga: :path: "../../../node_modules/react-native/ReactCommon/yoga" @@ -1989,9 +1984,8 @@ SPEC CHECKSUMS: RNGestureHandler: 939f21fabf5d45a725c0bf175eb819dd25cf2e70 RNReanimated: 9d20a811e6987cba268ef4f56242dfabd40e85c1 RNScreens: b03d696c70cc5235ce4587fcc27ae1a93a48f98c - RNSVG: 3d2bdcaef51c8071880a9c0072fe324f4423a3ba SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d - Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8 + Yoga: 2a45d7e59592db061217551fd3bbe2dd993817ae PODFILE CHECKSUM: debc09f5cfcbea21f946ca0be3faa5351e907125 diff --git a/packages/skia/src/external/reanimated/useAnimatedImageValue.ts b/packages/skia/src/external/reanimated/useAnimatedImageValue.ts index b525d97fab..d2232e4dc2 100644 --- a/packages/skia/src/external/reanimated/useAnimatedImageValue.ts +++ b/packages/skia/src/external/reanimated/useAnimatedImageValue.ts @@ -1,4 +1,3 @@ -import { useEffect } from "react"; import type { FrameInfo, SharedValue } from "react-native-reanimated"; import { useAnimatedImage } from "../../skia/core/AnimatedImage"; @@ -16,14 +15,10 @@ export const useAnimatedImageValue = ( const isPaused = paused ?? defaultPaused; const currentFrame = Rea.useSharedValue(null); const lastTimestamp = Rea.useSharedValue(-1); - const animatedImage = useAnimatedImage( - source, - (err) => { - console.error(err); - throw new Error(`Could not load animated image - got '${err.message}'`); - }, - false - ); + const animatedImage = useAnimatedImage(source, (err) => { + console.error(err); + throw new Error(`Could not load animated image - got '${err.message}'`); + }); const frameDuration = animatedImage?.currentFrameDuration() || DEFAULT_FRAME_DURATION; @@ -53,11 +48,5 @@ export const useAnimatedImageValue = ( // Update the last timestamp lastTimestamp.value = timestamp; }); - useEffect(() => { - return () => { - animatedImage?.dispose(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); return currentFrame; }; diff --git a/packages/skia/src/skia/core/AnimatedImage.ts b/packages/skia/src/skia/core/AnimatedImage.ts index e2f4661d8a..a49a63ee85 100644 --- a/packages/skia/src/skia/core/AnimatedImage.ts +++ b/packages/skia/src/skia/core/AnimatedImage.ts @@ -12,6 +12,5 @@ const animatedImgFactory = Skia.AnimatedImage.MakeAnimatedImageFromEncoded.bind( * */ export const useAnimatedImage = ( source: DataSourceParam, - onError?: (err: Error) => void, - managed = true -) => useRawData(source, animatedImgFactory, onError, managed); + onError?: (err: Error) => void +) => useRawData(source, animatedImgFactory, onError); diff --git a/packages/skia/src/skia/core/Data.ts b/packages/skia/src/skia/core/Data.ts index c694bff542..dc899c78d0 100644 --- a/packages/skia/src/skia/core/Data.ts +++ b/packages/skia/src/skia/core/Data.ts @@ -40,8 +40,7 @@ export const loadData = ( const useLoading = >( source: DataSourceParam, - loader: () => Promise, - manage = true + loader: () => Promise ) => { const mounted = useRef(false); const [data, setData] = useState(null); @@ -55,9 +54,6 @@ const useLoading = >( } }); return () => { - if (manage) { - dataRef.current?.dispose(); - } mounted.current = false; }; // eslint-disable-next-line react-hooks/exhaustive-deps @@ -84,7 +80,6 @@ export const useCollectionLoading = >( }); return () => { - dataRef.current?.forEach((instance) => instance?.dispose()); mounted.current = false; }; @@ -97,9 +92,8 @@ export const useCollectionLoading = >( export const useRawData = >( source: DataSourceParam, factory: (data: SkData) => T | null, - onError?: (err: Error) => void, - manage = true -) => useLoading(source, () => loadData(source, factory, onError), manage); + onError?: (err: Error) => void +) => useLoading(source, () => loadData(source, factory, onError)); const identity = (data: SkData) => data;