agent-rules-kit
Version:
CLI tool to bootstrap AI agent rules for multiple IDEs and frameworks. Generates optimized rules for Cursor, VS Code, Claude, and 6+ other AI coding assistants.
518 lines (418 loc) • 13.4 kB
Markdown
---
description: Core architectural concepts for React Native applications
globs: <root>/src/**/*.ts,<root>/src/**/*.tsx,<root>/src/**/*.js,<root>/src/**/*.jsx
alwaysApply: false
---
# React Native Architecture Concepts
This guide outlines the core architectural concepts for React Native applications in {projectPath}, covering the bridge architecture, native modules, and performance considerations.
## React Native Architecture Overview
### The Bridge Architecture
React Native uses a bridge to communicate between JavaScript and native code:
```
┌─────────────────┐ Bridge ┌─────────────────┐
│ JavaScript │ ◄─────────► │ Native Code │
│ (React) │ │ (iOS/Android) │
└─────────────────┘ └─────────────────┘
```
#### Key Components:
1. **JavaScript Thread**: Runs React components and business logic
2. **Native Thread**: Handles UI rendering and native operations
3. **Bridge**: Serializes data between JavaScript and native code
### New Architecture (Fabric + TurboModules)
React Native's new architecture improves performance:
```javascript
// TurboModules - Synchronous native module calls
import { TurboModuleRegistry } from 'react-native';
const MyTurboModule = TurboModuleRegistry.getEnforcing('MyTurboModule');
const result = MyTurboModule.synchronousMethod(); // No bridge!
// Fabric - New rendering system
// Components render directly to native views
```
## Component Architecture
### Component Hierarchy
```javascript
// App.tsx - Root component
export default function App() {
return (
<NavigationContainer>
<RootNavigator />
</NavigationContainer>
);
}
// Navigation structure
const RootNavigator = () => (
<Stack.Navigator>
<Stack.Screen name="TabNavigator" component={TabNavigator} />
<Stack.Screen name="Modal" component={ModalScreen} />
</Stack.Navigator>
);
const TabNavigator = () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
```
### Screen Components
```javascript
// screens/HomeScreen.tsx
import React, { useEffect } from 'react';
import { View, FlatList } from 'react-native';
import { useAppDispatch, useAppSelector } from '../hooks/redux';
import { fetchPosts } from '../store/slices/postsSlice';
export function HomeScreen() {
const dispatch = useAppDispatch();
const { posts, loading } = useAppSelector(state => state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, [dispatch]);
return (
<View style={styles.container}>
<FlatList
data={posts}
renderItem={({ item }) => <PostCard post={item} />}
keyExtractor={item => item.id}
refreshing={loading}
onRefresh={() => dispatch(fetchPosts())}
/>
</View>
);
}
```
## State Management Architecture
### Redux Toolkit Pattern
```javascript
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';
import authSlice from './slices/authSlice';
import postsSlice from './slices/postsSlice';
const persistConfig = {
key: 'root',
storage: AsyncStorage,
whitelist: ['auth'], // Only persist auth state
};
const persistedAuthReducer = persistReducer(persistConfig, authSlice);
export const store = configureStore({
reducer: {
auth: persistedAuthReducer,
posts: postsSlice,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
},
}),
});
export const persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
```
### Context API Pattern
```javascript
// contexts/ThemeContext.tsx
import React, { createContext, useContext, useReducer } from 'react';
import { Appearance } from 'react-native';
type Theme = 'light' | 'dark';
interface ThemeState {
theme: Theme;
colors: ColorScheme;
}
interface ThemeContextType extends ThemeState {
toggleTheme: () => void;
setTheme: (theme: Theme) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
const themeReducer = (state: ThemeState, action: any): ThemeState => {
switch (action.type) {
case 'TOGGLE_THEME':
return {
...state,
theme: state.theme === 'light' ? 'dark' : 'light',
colors: state.theme === 'light' ? darkColors : lightColors,
};
case 'SET_THEME':
return {
...state,
theme: action.payload,
colors: action.payload === 'light' ? lightColors : darkColors,
};
default:
return state;
}
};
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [state, dispatch] = useReducer(themeReducer, {
theme: Appearance.getColorScheme() || 'light',
colors: Appearance.getColorScheme() === 'dark' ? darkColors : lightColors,
});
const toggleTheme = () => dispatch({ type: 'TOGGLE_THEME' });
const setTheme = (theme: Theme) => dispatch({ type: 'SET_THEME', payload: theme });
return (
<ThemeContext.Provider value={{ ...state, toggleTheme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
};
```
## Navigation Architecture
### Navigation Structure
```javascript
// navigation/types.ts
export type RootStackParamList = {
Main: undefined;
Auth: undefined;
Modal: { data: any };
};
export type MainTabParamList = {
Home: undefined;
Search: undefined;
Profile: { userId?: string };
};
export type AuthStackParamList = {
Login: undefined;
Register: undefined;
ForgotPassword: undefined;
};
```
### Navigation Configuration
```javascript
// navigation/RootNavigator.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator<RootStackParamList>();
export function RootNavigator() {
const { isAuthenticated } = useAuth();
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
{isAuthenticated ? (
<>
<Stack.Screen name="Main" component={MainTabNavigator} />
<Stack.Screen
name="Modal"
component={ModalScreen}
options={{ presentation: 'modal' }}
/>
</>
) : (
<Stack.Screen name="Auth" component={AuthNavigator} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
```
## Data Flow Architecture
### Unidirectional Data Flow
```javascript
// hooks/useApi.ts
import { useState, useCallback } from 'react';
export function useApi<T>() {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const execute = useCallback(async (apiCall: () => Promise<T>) => {
try {
setLoading(true);
setError(null);
const result = await apiCall();
setData(result);
return result;
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'An error occurred';
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
}, []);
return { data, loading, error, execute };
}
```
### Service Layer
```javascript
// services/apiService.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
class ApiService {
private baseURL = 'https://api.example.com';
private async getAuthHeaders() {
const token = await AsyncStorage.getItem('authToken');
return {
'Content-Type': 'application/json',
...(token && { Authorization: `Bearer ${token}` }),
};
}
async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const headers = await this.getAuthHeaders();
const response = await fetch(`${this.baseURL}${endpoint}`, {
...options,
headers: { ...headers, ...options.headers },
});
if (!response.ok) {
throw new Error(`API Error: ${response.statusText}`);
}
return response.json();
}
// Specific API methods
async getPosts() {
return this.request<Post[]>('/posts');
}
async getUser(id: string) {
return this.request<User>(`/users/${id}`);
}
}
export const apiService = new ApiService();
```
## Performance Architecture
### Component Optimization
```javascript
// components/OptimizedList.tsx
import React, { memo, useMemo, useCallback } from 'react';
import { FlatList, ListRenderItem } from 'react-native';
interface OptimizedListProps<T> {
data: T[];
renderItem: ListRenderItem<T>;
keyExtractor: (item: T) => string;
}
function OptimizedListComponent<T>({
data,
renderItem,
keyExtractor,
}: OptimizedListProps<T>) {
const memoizedData = useMemo(() => data, [data]);
const memoizedRenderItem = useCallback<ListRenderItem<T>>(
(info) => renderItem(info),
[renderItem]
);
const getItemLayout = useCallback(
(data: T[] | null | undefined, index: number) => ({
length: 80, // Fixed item height
offset: 80 * index,
index,
}),
[]
);
return (
<FlatList
data={memoizedData}
renderItem={memoizedRenderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
removeClippedSubviews
maxToRenderPerBatch={10}
windowSize={10}
initialNumToRender={10}
updateCellsBatchingPeriod={50}
/>
);
}
export const OptimizedList = memo(OptimizedListComponent) as typeof OptimizedListComponent;
```
### Image Optimization
```javascript
// components/OptimizedImage.tsx
import React, { useState } from 'react';
import { Image, View, ActivityIndicator } from 'react-native';
import FastImage from 'react-native-fast-image';
interface OptimizedImageProps {
source: { uri: string };
style?: any;
placeholder?: React.ReactNode;
}
export const OptimizedImage: React.FC<OptimizedImageProps> = ({
source,
style,
placeholder,
}) => {
const [loading, setLoading] = useState(true);
return (
<View style={style}>
<FastImage
source={{
uri: source.uri,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
style={style}
onLoadStart={() => setLoading(true)}
onLoadEnd={() => setLoading(false)}
resizeMode={FastImage.resizeMode.cover}
/>
{loading && (
<View style={[style, { position: 'absolute', justifyContent: 'center', alignItems: 'center' }]}>
{placeholder || <ActivityIndicator />}
</View>
)}
</View>
);
};
```
## Security Architecture
### Secure Storage
```javascript
// utils/secureStorage.ts
import * as Keychain from 'react-native-keychain';
import AsyncStorage from '@react-native-async-storage/async-storage';
export class SecureStorage {
// Store sensitive data in Keychain
static async setSecureItem(key: string, value: string): Promise<void> {
await Keychain.setInternetCredentials(key, key, value);
}
static async getSecureItem(key: string): Promise<string | null> {
try {
const credentials = await Keychain.getInternetCredentials(key);
return credentials ? credentials.password : null;
} catch (error) {
return null;
}
}
// Store non-sensitive data in AsyncStorage
static async setItem(key: string, value: string): Promise<void> {
await AsyncStorage.setItem(key, value);
}
static async getItem(key: string): Promise<string | null> {
return AsyncStorage.getItem(key);
}
}
```
## Testing Architecture
### Test Structure
```javascript
// __tests__/components/UserCard.test.tsx
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import { UserCard } from '../../src/components/UserCard';
const mockUser = {
id: '1',
name: 'John Doe',
email: 'john@example.com',
avatar: 'https://example.com/avatar.jpg',
};
describe('UserCard', () => {
it('renders user information correctly', () => {
const { getByText, getByTestId } = render(<UserCard user={mockUser} />);
expect(getByText('John Doe')).toBeTruthy();
expect(getByText('john@example.com')).toBeTruthy();
expect(getByTestId('user-avatar')).toBeTruthy();
});
it('calls onPress when card is tapped', () => {
const mockOnPress = jest.fn();
const { getByTestId } = render(
<UserCard user={mockUser} onPress={mockOnPress} />
);
fireEvent.press(getByTestId('user-card'));
expect(mockOnPress).toHaveBeenCalledWith(mockUser);
});
});
```
This architecture provides a solid foundation for scalable React Native applications with clear separation of concerns, optimized performance, and maintainable code structure.