Complete guide for developing the Loop application.
- Project Structure
- Development Workflow
- Code Style
- Component Development
- State Management
- API Integration
- Testing
- Debugging
- Performance
loop/
├── app/ # Expo Router pages (file-based routing)
│ ├── (tabs)/ # Tab navigation screens
│ │ ├── index.tsx # Home screen
│ │ ├── chat.tsx # Chat screen
│ │ └── dashboard.tsx # Dashboard screen
│ ├── auth/ # Authentication screens
│ ├── records/ # Record management screens
│ ├── camera.tsx # Camera screen
│ ├── login.tsx # Login screen
│ └── _layout.tsx # Root layout
├── src/ # Source code
│ ├── components/ # Reusable UI components
│ ├── features/ # Feature-specific code
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Utilities and helpers
│ ├── providers/ # Context providers
│ ├── services/ # API and external services
│ └── types/ # TypeScript type definitions
├── docs/ # Documentation
├── ios/ # iOS native code
├── android/ # Android native code
└── assets/ # Static assets
app/- All screens and routes (Expo Router convention)src/components/- Shared UI components (Button, Card, etc.)src/features/- Feature modules (records, chat, etc.)src/providers/- React Context providers (Theme, Auth, etc.)src/services/- External integrations (Supabase, OpenAI, etc.)
npm start# iOS Simulator
npm run ios
# Android Emulator
npm run android
# Web Browser
npm run web
# Real Device
npx expo run:ios --device- Edit files in
app/orsrc/ - Save to trigger Fast Refresh
- Changes appear instantly in the app
git add .
git commit -m "feat: add new feature"
git pushAll code must be TypeScript with proper typing:
// ✅ Good
interface User {
id: string;
email: string;
name?: string;
}
function getUser(id: string): Promise<User> {
// ...
}
// ❌ Bad
function getUser(id: any): any {
// ...
}Use functional components with hooks:
// ✅ Good
export function MyComponent({ title }: { title: string }) {
const [count, setCount] = useState(0);
return <Text>{title}: {count}</Text>;
}
// ❌ Bad
export default class MyComponent extends Component {
// ...
}- Components: PascalCase -
MyComponent.tsx - Hooks: camelCase with 'use' prefix -
useMyHook.ts - Utilities: camelCase -
formatDate.ts - Types: PascalCase -
User.ts - Screens: camelCase -
index.tsx,login.tsx
// 1. React imports
import { useState, useEffect } from 'react';
// 2. React Native imports
import { View, Text } from 'react-native';
// 3. Third-party imports
import { useRouter } from 'expo-router';
// 4. Local imports
import { useTheme } from '../providers/ThemeProvider';
import { Button } from '../components/Button';- Create file in
src/components/:
// src/components/MyButton.tsx
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
import { useTheme } from '../providers/ThemeProvider';
interface MyButtonProps {
title: string;
onPress: () => void;
variant?: 'primary' | 'secondary';
}
export function MyButton({ title, onPress, variant = 'primary' }: MyButtonProps) {
const { theme } = useTheme();
return (
<TouchableOpacity
style={[
styles.button,
{ backgroundColor: variant === 'primary' ? theme.colors.primary : theme.colors.secondary }
]}
onPress={onPress}
>
<Text style={styles.text}>{title}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
button: {
padding: 12,
borderRadius: 8,
alignItems: 'center',
},
text: {
color: 'white',
fontWeight: '600',
},
});- Export from index (if needed):
// src/components/index.ts
export { MyButton } from './MyButton';
export { Card } from './Card';
// ...- Use in screens:
import { MyButton } from '../src/components/MyButton';
export default function MyScreen() {
return <MyButton title="Click me" onPress={() => console.log('Clicked')} />;
}Always use the theme provider for colors and spacing:
import { useTheme } from '../src/providers/ThemeProvider';
export function MyComponent() {
const { theme } = useTheme();
return (
<View style={{
backgroundColor: theme.colors.background,
padding: theme.spacing.md
}}>
<Text style={{ color: theme.colors.text }}>Hello</Text>
</View>
);
}For component-specific state:
function Counter() {
const [count, setCount] = useState(0);
return (
<Button onPress={() => setCount(count + 1)}>
Count: {count}
</Button>
);
}For app-wide state, use Context providers:
// src/providers/MyProvider.tsx
import { createContext, useContext, useState } from 'react';
interface MyContextValue {
data: string;
setData: (data: string) => void;
}
const MyContext = createContext<MyContextValue | null>(null);
export function MyProvider({ children }: { children: React.ReactNode }) {
const [data, setData] = useState('');
return (
<MyContext.Provider value={{ data, setData }}>
{children}
</MyContext.Provider>
);
}
export function useMyContext() {
const context = useContext(MyContext);
if (!context) throw new Error('useMyContext must be used within MyProvider');
return context;
}Extract reusable logic into custom hooks:
// src/hooks/useRecords.ts
export function useRecords() {
const [records, setRecords] = useState<Record[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadRecords();
}, []);
async function loadRecords() {
setLoading(true);
const data = await fetchRecords();
setRecords(data);
setLoading(false);
}
return { records, loading, refresh: loadRecords };
}Use the Supabase client from src/lib/supabase.ts:
import { supabase } from '../lib/supabase';
// Query data
const { data, error } = await supabase
.from('records')
.select('*')
.eq('user_id', userId);
// Insert data
const { data, error } = await supabase
.from('records')
.insert({ title: 'New Record' });
// Update data
const { data, error } = await supabase
.from('records')
.update({ title: 'Updated' })
.eq('id', recordId);Use the AI service from src/services/ai.ts:
import { analyzeImage } from '../services/ai';
const result = await analyzeImage(base64Image);
console.log(result.question);
console.log(result.correctAnswer);Always handle errors gracefully:
try {
const data = await fetchData();
setData(data);
} catch (error) {
console.error('Failed to fetch:', error);
Alert.alert('Error', error instanceof Error ? error.message : 'Unknown error');
}npm run typechecknpm run lint- Test on iOS simulator
- Test on Android emulator
- Test on real device
- Test in light and dark mode
- Test with slow network
- Test offline behavior
- Test error states
- Test loading states
console.log('Debug:', value);
console.error('Error:', error);
console.warn('Warning:', warning);- Shake device to open developer menu
- Select "Debug" to connect to Chrome DevTools
- Open Chrome:
chrome://inspect
Access at http://localhost:8081 when Metro is running.
Use React Native Debugger or Flipper to inspect network requests.
# Clear cache
npx expo start --clear
# Reset Metro bundler
npx expo start --reset-cache
# View logs
npx react-native log-ios
npx react-native log-android- Use React.memo for expensive components:
export const ExpensiveComponent = React.memo(({ data }) => {
// ...
});- Use useCallback for functions passed as props:
const handlePress = useCallback(() => {
// ...
}, [dependencies]);- Use useMemo for expensive calculations:
const sortedData = useMemo(() => {
return data.sort((a, b) => a.value - b.value);
}, [data]);- Optimize images:
<Image
source={{ uri: imageUrl }}
style={{ width: 100, height: 100 }}
resizeMode="cover"
/>- Use FlatList for long lists:
<FlatList
data={items}
renderItem={({ item }) => <Item data={item} />}
keyExtractor={item => item.id}
windowSize={10}
/>- Use TypeScript for all new code
- Follow the existing code structure
- Write descriptive commit messages
- Handle errors gracefully
- Use theme colors and spacing
- Test on multiple platforms
- Keep components small and focused
- Use custom hooks for reusable logic
- Don't use
anytype - Don't commit
.envfile - Don't hardcode colors or spacing
- Don't ignore TypeScript errors
- Don't use class components
- Don't skip error handling
- Don't commit commented-out code
- Don't use inline styles excessively
type(scope): subject
body (optional)
Types:
feat: New featurefix: Bug fixdocs: Documentationstyle: Code style changesrefactor: Code refactoringtest: Testschore: Maintenance
Examples:
feat(camera): add AI question recognition
fix(auth): resolve login redirect issue
docs(readme): update installation steps
- Check existing documentation
- Search for similar issues
- Ask in team chat
- Create detailed issue with:
- Steps to reproduce
- Expected behavior
- Actual behavior
- Screenshots/logs
- Environment details