Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

### React Component Structure

- Use arrow functions with extracted Props type
- Use arrow functions with extracted Props interfaces
- Don't use `React.FC` type
- Prefer types to interfaces for props
- Prefer interfaces to types for Props
- To extract Props type, use `ComponentProps<typeof MyComponent>`
- No need to forwardRef, as the project uses React 19

Expand Down
5 changes: 3 additions & 2 deletions src/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import '@formatjs/intl-getcanonicallocales/polyfill';
import 'intl-pluralrules';
import '../infra/i18n';

import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
import * as Sentry from '@sentry/react-native';
Expand All @@ -19,6 +20,7 @@ import {
useAppScreenTracking,
useAppStateTracking,
} from '$features/navigation';
import { useAppFocusManager } from '$infra/api';
import { persistOptions, queryClient } from '$infra/api/queryClient';
import { ErrorMonitoring } from '$infra/monitoring';
import { ProductTrackingProvider } from '$infra/productTracking';
Expand All @@ -35,8 +37,6 @@ import {
useRoutingInstrumentation,
} from '$shared/hooks';

import '../infra/i18n';

// Sentry is initialized here so that it runs before Sentry.wrap()
ErrorMonitoring.init();

Expand All @@ -62,6 +62,7 @@ const RootLayout = () => {
useCheckNetworkStateOnMount();
useAppStateTracking();
useAppScreenTracking();
useAppFocusManager();

return (
<StrictMode>
Expand Down
2 changes: 2 additions & 0 deletions src/infra/api/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useAppFocusManager';
export * from './useRefreshOnFocus';
21 changes: 21 additions & 0 deletions src/infra/api/hooks/useAppFocusManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { focusManager } from '@tanstack/react-query';
import type { AppStateStatus } from 'react-native';
import { AppState, Platform } from 'react-native';

import { useRunOnMount } from '$shared/hooks';

export const useAppFocusManager = () => {
useRunOnMount(() => {
const onAppStateChange = (status: AppStateStatus) => {
if (Platform.OS !== 'web') {
focusManager.setFocused(status === 'active');
}
};

const subscription = AppState.addEventListener('change', onAppStateChange);

return () => {
subscription.remove();
};
});
};
37 changes: 37 additions & 0 deletions src/infra/api/hooks/useRefreshOnFocus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useQueryClient } from '@tanstack/react-query';
import { useFocusEffect } from 'expo-router';
import { useCallback, useRef } from 'react';

interface UseRefreshOnFocusOptions {
shouldRefetchAll?: boolean;
}

export const useRefreshOnFocus = (options?: UseRefreshOnFocusOptions) => {
const { shouldRefetchAll = false } = options ?? {};

const isFirstTimeRef = useRef(true);

const queryClient = useQueryClient();

useFocusEffect(
useCallback(() => {
// Skip the first focus to avoid double-fetching on initial mount
if (isFirstTimeRef.current) {
isFirstTimeRef.current = false;

return;
}

if (shouldRefetchAll) {
void queryClient.refetchQueries({
type: 'active',
});
} else {
void queryClient.refetchQueries({
stale: true,
type: 'active',
});
}
}, [queryClient, shouldRefetchAll]),
);
};
1 change: 1 addition & 0 deletions src/infra/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './hooks';