@umituz/react-native-bottom-sheet 1.1.7 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-bottom-sheet",
3
- "version": "1.1.7",
3
+ "version": "1.1.8",
4
4
  "description": "Modern, performant bottom sheets for React Native with preset configurations, keyboard handling, and smooth animations",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
package/src/index.ts CHANGED
@@ -108,3 +108,8 @@ export {
108
108
 
109
109
  // Re-export BottomSheetModalProvider for convenience
110
110
  export { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
111
+
112
+ // Safe provider that ensures Reanimated is ready before rendering
113
+ export {
114
+ SafeBottomSheetModalProvider,
115
+ } from './presentation/components/SafeBottomSheetModalProvider';
@@ -156,66 +156,7 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
156
156
  const sheetRef = React.useRef<GorhomBottomSheet>(null);
157
157
  const [isMounted, setIsMounted] = useState(false);
158
158
 
159
- // Ensure component is mounted after Reanimated is ready
160
- // This prevents layoutState.get errors during initial render
161
- // @gorhom/bottom-sheet uses useAnimatedDetents which accesses layoutState.get
162
- // during initialization, so we need to wait for Reanimated to be fully ready
163
- useEffect(() => {
164
- // Use a longer delay to ensure Reanimated is fully initialized
165
- // This is critical because @gorhom/bottom-sheet's internal hooks
166
- // access layoutState.get immediately when the component renders
167
- const timer = setTimeout(() => {
168
- // Use multiple animation frames to ensure Reanimated worklets are ready
169
- requestAnimationFrame(() => {
170
- requestAnimationFrame(() => {
171
- requestAnimationFrame(() => {
172
- setIsMounted(true);
173
- });
174
- });
175
- });
176
- }, 300); // Increased delay to 300ms to ensure Reanimated is fully initialized
177
-
178
- return () => {
179
- clearTimeout(timer);
180
- };
181
- }, []);
182
-
183
- // Expose ref methods
184
- React.useImperativeHandle(ref, () => ({
185
- snapToIndex: (index: number) => {
186
- if (isMounted) {
187
- sheetRef.current?.snapToIndex(index);
188
- }
189
- },
190
- snapToPosition: (position: string | number) => {
191
- if (isMounted) {
192
- sheetRef.current?.snapToPosition(position);
193
- }
194
- },
195
- expand: () => {
196
- if (isMounted) {
197
- sheetRef.current?.expand();
198
- }
199
- },
200
- collapse: () => {
201
- if (isMounted) {
202
- sheetRef.current?.collapse();
203
- }
204
- },
205
- close: () => {
206
- if (isMounted) {
207
- sheetRef.current?.close();
208
- }
209
- },
210
- }));
211
-
212
- // Don't compute config or callbacks until mounted to prevent early hook execution
213
- // This ensures @gorhom/bottom-sheet's internal hooks don't run before Reanimated is ready
214
- if (!isMounted) {
215
- return null;
216
- }
217
-
218
- // Get configuration from preset or custom
159
+ // Get configuration from preset or custom (must be before useImperativeHandle)
219
160
  const config: BottomSheetConfig = useMemo(() => {
220
161
  if (customSnapPoints) {
221
162
  return BottomSheetUtils.createConfig({
@@ -244,7 +185,31 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
244
185
  enableDynamicSizing,
245
186
  ]);
246
187
 
247
- // Render backdrop component
188
+ // Ensure component is mounted after Reanimated is ready
189
+ // This prevents layoutState.get errors during initial render
190
+ // @gorhom/bottom-sheet uses useAnimatedDetents which accesses layoutState.get
191
+ // during initialization, so we need to wait for Reanimated to be fully ready
192
+ useEffect(() => {
193
+ // Use a longer delay to ensure Reanimated is fully initialized
194
+ // This is critical because @gorhom/bottom-sheet's internal hooks
195
+ // access layoutState.get immediately when the component renders
196
+ const timer = setTimeout(() => {
197
+ // Use multiple animation frames to ensure Reanimated worklets are ready
198
+ requestAnimationFrame(() => {
199
+ requestAnimationFrame(() => {
200
+ requestAnimationFrame(() => {
201
+ setIsMounted(true);
202
+ });
203
+ });
204
+ });
205
+ }, 300); // Increased delay to 300ms to ensure Reanimated is fully initialized
206
+
207
+ return () => {
208
+ clearTimeout(timer);
209
+ };
210
+ }, []);
211
+
212
+ // Render backdrop component (must be before early return to maintain hook order)
248
213
  const renderBackdrop = useCallback(
249
214
  (props: BottomSheetBackdropProps) =>
250
215
  enableBackdrop ? (
@@ -259,7 +224,7 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
259
224
  [enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
260
225
  );
261
226
 
262
- // Handle sheet changes
227
+ // Handle sheet changes (must be before early return to maintain hook order)
263
228
  const handleSheetChange = useCallback(
264
229
  (index: number) => {
265
230
  onChange?.(index);
@@ -270,6 +235,42 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
270
235
  [onChange, onClose]
271
236
  );
272
237
 
238
+ // Expose ref methods (must be before early return to maintain hook order)
239
+ React.useImperativeHandle(ref, () => ({
240
+ snapToIndex: (index: number) => {
241
+ if (isMounted) {
242
+ sheetRef.current?.snapToIndex(index);
243
+ }
244
+ },
245
+ snapToPosition: (position: string | number) => {
246
+ if (isMounted) {
247
+ sheetRef.current?.snapToPosition(position);
248
+ }
249
+ },
250
+ expand: () => {
251
+ if (isMounted) {
252
+ sheetRef.current?.expand();
253
+ }
254
+ },
255
+ collapse: () => {
256
+ if (isMounted) {
257
+ sheetRef.current?.collapse();
258
+ }
259
+ },
260
+ close: () => {
261
+ if (isMounted) {
262
+ sheetRef.current?.close();
263
+ }
264
+ },
265
+ }));
266
+
267
+ // Don't render until mounted to prevent early hook execution
268
+ // This ensures @gorhom/bottom-sheet's internal hooks don't run before Reanimated is ready
269
+ // IMPORTANT: All hooks must be called before this early return to maintain hook order
270
+ if (!isMounted) {
271
+ return null;
272
+ }
273
+
273
274
  return (
274
275
  <GorhomBottomSheet
275
276
  ref={sheetRef}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * SafeBottomSheetModalProvider
3
+ *
4
+ * Enhanced BottomSheetModalProvider that ensures Reanimated is fully
5
+ * initialized before rendering. This prevents "containerLayoutState.get
6
+ * is not a function" errors that occur when @gorhom/bottom-sheet's
7
+ * internal hooks access Reanimated's layoutState before it's ready.
8
+ *
9
+ * Usage:
10
+ * ```tsx
11
+ * import { SafeBottomSheetModalProvider } from '@umituz/react-native-bottom-sheet';
12
+ *
13
+ * <SafeBottomSheetModalProvider>
14
+ * <App />
15
+ * </SafeBottomSheetModalProvider>
16
+ * ```
17
+ */
18
+
19
+ import React, { useState, useEffect } from 'react';
20
+ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
21
+
22
+ interface SafeBottomSheetModalProviderProps {
23
+ children: React.ReactNode;
24
+ }
25
+
26
+ /**
27
+ * SafeBottomSheetModalProvider
28
+ *
29
+ * Delays rendering of BottomSheetModalProvider until Reanimated is ready.
30
+ * This prevents layoutState.get errors during initial render.
31
+ */
32
+ export const SafeBottomSheetModalProvider: React.FC<SafeBottomSheetModalProviderProps> = ({
33
+ children,
34
+ }) => {
35
+ const [isReanimatedReady, setIsReanimatedReady] = useState(false);
36
+
37
+ useEffect(() => {
38
+ // Wait for Reanimated to be fully initialized
39
+ // @gorhom/bottom-sheet uses useAnimatedDetents which accesses layoutState.get
40
+ // during initialization, so we need to wait for Reanimated to be fully ready
41
+ const timer = setTimeout(() => {
42
+ // Use multiple animation frames to ensure Reanimated worklets are ready
43
+ requestAnimationFrame(() => {
44
+ requestAnimationFrame(() => {
45
+ requestAnimationFrame(() => {
46
+ setIsReanimatedReady(true);
47
+ });
48
+ });
49
+ });
50
+ }, 300); // Delay to ensure Reanimated is fully initialized
51
+
52
+ return () => {
53
+ clearTimeout(timer);
54
+ };
55
+ }, []);
56
+
57
+ // Don't render BottomSheetModalProvider until Reanimated is ready
58
+ if (!isReanimatedReady) {
59
+ return <>{children}</>;
60
+ }
61
+
62
+ return <BottomSheetModalProvider>{children}</BottomSheetModalProvider>;
63
+ };
64
+