From 1a5a790db0a9be76bd1fe3c553aabb9c80688549 Mon Sep 17 00:00:00 2001 From: Amadeus Demarzi Date: Wed, 6 Dec 2023 02:54:20 -0800 Subject: [PATCH] fix: move DelayedFreeze setImmediate into an effect (#1980) ## Description Executing side effects in render is usually considered bad manners in react land, and given concurrent rendering could have unexpected results if renders are thrown away. ## Changes This change makes it so setImmediate fires in an effect, and also cleans up after itself if for whatever reason the callback isn't fired. (although I think it's probably not possible with how setImmediate is implemented) ## Screenshots / GIFs N/A ## Test code and steps to reproduce We (at Discord) ran essentially this patch for probably over a year now without issue and figured it might be good to give back. Here's an issue related to it: https://github.com/software-mansion/react-native-screens/issues/1198 ## Checklist - [ ] Included code example that can be used to test this change - [ ] Updated TS types - [ ] Updated documentation: - [ ] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [ ] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [ ] Ensured that CI passes --- src/index.native.tsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/index.native.tsx b/src/index.native.tsx index a51898a853..8e00ebb1f1 100644 --- a/src/index.native.tsx +++ b/src/index.native.tsx @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -import React, { PropsWithChildren, ReactNode } from 'react'; +import React, { useEffect, PropsWithChildren, ReactNode } from 'react'; import { Animated, Image, @@ -194,13 +194,14 @@ function DelayedFreeze({ freeze, children }: FreezeWrapperProps) { // flag used for determining whether freeze should be enabled const [freezeState, setFreezeState] = React.useState(false); - if (freeze !== freezeState) { - // setImmediate is executed at the end of the JS execution block. - // Used here for changing the state right after the render. - setImmediate(() => { + useEffect(() => { + const id = setImmediate(() => { setFreezeState(freeze); }); - } + return () => { + clearImmediate(id); + } + }, [freeze]) return {children}; }