Expo Managed WorkflowでのReact Nativeアプリ実装ベストプラクティス。Expo Router、Zustand、React Query、TypeScript型定義、UIパターンを提供します。
Expo Managed Workflow での React Native アプリ実装ベストプラクティス。
/app
_layout.tsx ← Root Layout
index.tsx ← 投稿一覧(ホーム)
/posts
[id].tsx ← 投稿詳細
// app/_layout.tsx
export default function RootLayout() {
return (
<QueryClientProvider client={queryClient}>
<Stack>
<Stack.Screen name="index" options={{ title: 'Posts' }} />
<Stack.Screen name="posts/[id]" options={{ title: 'Post Detail' }} />
</Stack>
</QueryClientProvider>
);
}
// src/store/useNotificationStore.ts
interface NotificationStore {
lastPostId: string | null;
setLastPostId: (id: string) => void;
}
export const useNotificationStore = create<NotificationStore>((set) => ({
lastPostId: null,
setLastPostId: (id) => set({ lastPostId: id }),
}));
// src/api/client.ts
import axios from 'axios';
import Constants from 'expo-constants';
const apiClient = axios.create({
baseURL: Constants.expoConfig?.extra?.apiBaseUrl,
timeout: 10000,
});
export default apiClient;
// src/api/posts.ts
export function usePostsQuery() {
return useQuery({
queryKey: ['posts'],
queryFn: async () => {
const { data } = await apiClient.get<Post[]>('/posts');
return data;
},
});
}
export function usePostDetailQuery(id: string) {
return useQuery({
queryKey: ['posts', id],
queryFn: async () => {
const { data } = await apiClient.get<Post>(`/posts/${id}`);
return data;
},
enabled: !!id,
});
}
src/types/api.ts に集約src/types/ に配置<FlatList
data={posts}
renderItem={renderItem}
refreshControl={
<RefreshControl refreshing={isRefetching} onRefresh={refetch} />
}
ListEmptyComponent={<EmptyState />}
/>
isLoading: スケルトン or スピナー表示isError: エラーメッセージ + リトライボタン