react-native · alinaqi — скилл для Claude Code: паттерны мобильных приложений
Скилл задаёт структуру проекта, паттерны компонентов и правила тестирования для React Native. Пригодится, когда агент правит .tsx или код под iOS и Android.
npx skills add alinaqi/maggy --skill react-native -a claude-codeЧто делает react-native
Скилл включается, когда работа идёт с файлами **/*.tsx, **/*.jsx, каталогами ios/, android/ или app.json. Он описывает структуру проекта: чистую бизнес-логику в src/core/, переиспользуемые компоненты, экраны со своими хуками, навигацию, стор и утилиты.
Внутри — набор правил по коду. Функциональные компоненты, логика вынесена в хуки, интерфейс пропсов прописан явно. Локальный стейт через useState, глобальный — на Zustand, серверный — на React Query. Для тестов компонентов и хуков используется React Native Testing Library.
Платформенные различия скилл советует держать через Platform.select, а сложные случаи разносить по файлам Component.ios.tsx и Component.android.tsx. Отдельный список антипаттернов: инлайн-стили, логика в рендере, индекс как ключ в списках, мутация стейта напрямую. Скилл не вызывается вручную — он подключается сам по путям.
Какие задачи решает
- разложить экран на презентационный компонент и хук с логикой
- вынести общий код под iOS и Android в отдельные файлы
- покрыть компонент тестом на нажатие и на заблокированное состояние
- перевести серверные данные на React Query с инвалидацией кэша
- убрать инлайн-стили и анонимные функции из пропсов
Примеры запросов агенту
- Разбей HomeScreen на компонент и хук useHome, логику убери из рендера
- Сделай тень через Platform.select для iOS и Android
- Напиши тест для Button: нажатие вызывает onPress, а в disabled — нет
Как установить react-native
1Через Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a claude-code2Вручную: положить файлы
~/.claude/skills/react-native/SKILL.mdПапка скилла целиком; для одного проекта — .claude/skills/react-native/
1Через Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a codex2Вручную: положить файлы
.agents/skills/react-native/SKILL.mdДля всех проектов — ~/.agents/skills/
1Через Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a cursor2Вручную: положить файлы
.cursor/skills/react-native/SKILL.mdCursor читает и .agents/skills/, и .claude/skills/
1GitHub CLI
gh skill install alinaqi/maggy react-native2Вручную: положить файлы
.github/skills/react-native/SKILL.md1Через Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a gemini-cli2Командой платформы
gemini skills install https://github.com/alinaqi/maggy --path skills/react-nativeВручную: положить файлы
.devin/skills/react-native/SKILL.mdСтарый путь .windsurf/skills/ тоже читается
Через Skills CLI (Vercel)
npx skills add alinaqi/maggy --skill react-native -a opencodeClaude.ai
Упакуйте папку в zip и загрузите в Settings → Capabilities → Skills
По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов
Из того же набора32
Другие версии react-native
Похожие
Рядом в теме «Мобильная разработка»
Всё по теме «Мобильная разработка» →Вопросы о react-native
Как установить react-native в Claude?
Выполни npx skills add alinaqi/maggy --skill react-native -a claude-code. Файл скилла окажется в ~/.claude/skills/react-native/SKILL.md.
Что делает скилл react-native?
Задаёт агенту структуру проекта и паттерны для React Native: функциональные компоненты, хуки для логики, Zustand и React Query для стейта, тесты через React Native Testing Library.
Когда скилл включается сам?
Он привязан к путям **/*.tsx, **/*.jsx, ios/**, android/** и app.json. Вручную вызывать его не нужно.
Исходный текст
SKILL.md · MIT
---
name: react-native
description: React Native mobile patterns, platform-specific code
when-to-use: When working on React Native mobile app code
user-invocable: false
paths: ["**/*.tsx", "**/*.jsx", "ios/**", "android/**", "app.json"]
effort: medium
---
# React Native Skill
---
## Project Structure
```
project/
├── src/
│ ├── core/ # Pure business logic (no React)
│ │ ├── types.ts
│ │ └── services/
│ ├── components/ # Reusable UI components
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ └── index.ts
│ │ └── index.ts # Barrel export
│ ├── screens/ # Screen components
│ │ ├── Home/
│ │ │ ├── HomeScreen.tsx
│ │ │ ├── useHome.ts # Screen-specific hook
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── navigation/ # Navigation configuration
│ ├── hooks/ # Shared custom hooks
│ ├── store/ # State management
│ └── utils/ # Utilities
├── __tests__/
├── android/
├── ios/
└── CLAUDE.md
```
---
## Component Patterns
### Functional Components Only
```typescript
// Good - simple, testable
interface ButtonProps {
label: string;
onPress: () => void;
disabled?: boolean;
}
export function Button({ label, onPress, disabled = false }: ButtonProps): JSX.Element {
return (
<Pressable onPress={onPress} disabled={disabled}>
<Text>{label}</Text>
</Pressable>
);
}
```
### Extract Logic to Hooks
```typescript
// useHome.ts - all logic here
export function useHome() {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const refresh = useCallback(async () => {
setLoading(true);
const data = await fetchItems();
setItems(data);
setLoading(false);
}, []);
return { items, loading, refresh };
}
// HomeScreen.tsx - pure presentation
export function HomeScreen(): JSX.Element {
const { items, loading, refresh } = useHome();
return (
<ItemList items={items} loading={loading} onRefresh={refresh} />
);
}
```
### Props Interface Always Explicit
```typescript
// Always define props interface, even if simple
interface ItemCardProps {
item: Item;
onPress: (id: string) => void;
}
export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element {
...
}
```
---
## State Management
### Local State First
```typescript
// Start with useState, escalate only when needed
const [value, setValue] = useState('');
```
### Zustand for Global State (if needed)
```typescript
// store/useAppStore.ts
import { create } from 'zustand';
interface AppState {
user: User | null;
setUser: (user: User | null) => void;
}
export const useAppStore = create<AppState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
```
### React Query for Server State
```typescript
// hooks/useItems.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
export function useItems() {
return useQuery({
queryKey: ['items'],
queryFn: fetchItems,
});
}
export function useCreateItem() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createItem,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['items'] });
},
});
}
```
---
## Testing
### Component Testing with React Native Testing Library
```typescript
import { render, fireEvent } from '@testing-library/react-native';
import { Button } from './Button';
describe('Button', () => {
it('calls onPress when pressed', () => {
const onPress = jest.fn();
const { getByText } = render(<Button label="Click me" onPress={onPress} />);
fireEvent.press(getByText('Click me'));
expect(onPress).toHaveBeenCalledTimes(1);
});
it('does not call onPress when disabled', () => {
const onPress = jest.fn();
const { getByText } = render(<Button label="Click me" onPress={onPress} disabled />);
fireEvent.press(getByText('Click me'));
expect(onPress).not.toHaveBeenCalled();
});
});
```
### Hook Testing
```typescript
import { renderHook, act } from '@testing-library/react-hooks';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
```
---
## Platform-Specific Code
### Use Platform.select Sparingly
```typescript
import { Platform } from 'react-native';
const styles = StyleSheet.create({
shadow: Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
},
android: {
elevation: 2,
},
}),
});
```
### Separate Files for Complex Differences
```
Component/
├── Component.tsx # Shared logic
├── Component.ios.tsx # iOS-specific
├── Component.android.tsx # Android-specific
└── index.ts
```
---
## React Native Anti-Patterns
- ❌ Inline styles - use StyleSheet.create
- ❌ Logic in render - extract to hooks
- ❌ Deep component nesting - flatten hierarchy
- ❌ Anonymous functions in props - use useCallback
- ❌ Index as key in lists - use stable IDs
- ❌ Direct state mutation - always use setter
- ❌ Mixing business logic with UI - keep core/ pure
- ❌ Ignoring TypeScript errors - fix them
- ❌ Large components - split into smaller pieces
© alinaqi · MIT · alinaqi/maggy