about summary refs log tree commit diff
diff options
context:
space:
mode:
authordan <dan.abramov@gmail.com>2023-11-17 02:01:51 +0000
committerGitHub <noreply@github.com>2023-11-16 18:01:51 -0800
commit3043b324681f1702ca53831701fb5cecd14c0efb (patch)
tree7a8ba883a158870b95e94af1c11afa3913398ee3
parentc03c744008bc15be807ccb172801268bd24dae53 (diff)
downloadvoidsky-3043b324681f1702ca53831701fb5cecd14c0efb.tar.zst
Fix jumpy modal regression (#1945)
-rw-r--r--src/lib/hooks/useNonReactiveCallback.ts23
-rw-r--r--src/view/com/modals/Modal.tsx16
2 files changed, 33 insertions, 6 deletions
diff --git a/src/lib/hooks/useNonReactiveCallback.ts b/src/lib/hooks/useNonReactiveCallback.ts
new file mode 100644
index 000000000..4b3d6abb9
--- /dev/null
+++ b/src/lib/hooks/useNonReactiveCallback.ts
@@ -0,0 +1,23 @@
+import {useCallback, useInsertionEffect, useRef} from 'react'
+
+// This should be used sparingly. It erases reactivity, i.e. when the inputs
+// change, the function itself will remain the same. This means that if you
+// use this at a higher level of your tree, and then some state you read in it
+// changes, there is no mechanism for anything below in the tree to "react"
+// to this change (e.g. by knowing to call your function again).
+//
+// Also, you should avoid calling the returned function during rendering
+// since the values captured by it are going to lag behind.
+export function useNonReactiveCallback<T extends Function>(fn: T): T {
+  const ref = useRef(fn)
+  useInsertionEffect(() => {
+    ref.current = fn
+  }, [fn])
+  return useCallback(
+    (...args: any) => {
+      const latestFn = ref.current
+      return latestFn(...args)
+    },
+    [ref],
+  ) as unknown as T
+}
diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx
index d834db40b..a3e6fb9e5 100644
--- a/src/view/com/modals/Modal.tsx
+++ b/src/view/com/modals/Modal.tsx
@@ -9,6 +9,7 @@ import {navigate} from '../../../Navigation'
 import once from 'lodash.once'
 
 import {useModals, useModalControls} from '#/state/modals'
+import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
 import * as ConfirmModal from './Confirm'
 import * as EditProfileModal from './EditProfile'
 import * as ProfilePreviewModal from './ProfilePreview'
@@ -50,12 +51,15 @@ export function ModalsContainer() {
 
   const navigateOnce = once(navigate)
 
-  const onBottomSheetAnimate = (_fromIndex: number, toIndex: number) => {
-    if (activeModal?.name === 'profile-preview' && toIndex === 1) {
-      // begin loading the profile screen behind the scenes
-      navigateOnce('Profile', {name: activeModal.did})
-    }
-  }
+  // It seems like the bottom sheet bugs out when this callback changes.
+  const onBottomSheetAnimate = useNonReactiveCallback(
+    (_fromIndex: number, toIndex: number) => {
+      if (activeModal?.name === 'profile-preview' && toIndex === 1) {
+        // begin loading the profile screen behind the scenes
+        navigateOnce('Profile', {name: activeModal.did})
+      }
+    },
+  )
   const onBottomSheetChange = async (snapPoint: number) => {
     if (snapPoint === -1) {
       closeModal()