关于 react-native
面向 React Native 开发的技能,介绍项目组织、组件与状态管理模式、测试实践及平台专属代码约定。适用于指导 React Native 应用的结构设计与实现。
主要功能
- 01项目结构指导
- 02函数组件模式
- 03通过 Hook 拆分逻辑
- 04状态管理建议
- 05组件与 Hook 测试
- 06平台专属代码约定
适用场景
- 01组织 React Native 项目
- 02构建带显式属性定义的可复用组件
- 03测试组件与 Hook
- 04拆分平台专属实现
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ alinaqi/ maggy --skill react-native安装后在 Claude Code 中输入/react-native确认已加载。
详细介绍
React Native 技能
项目结构
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
组件模式
仅使用函数组件
// 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>
);
}
将逻辑提取到 Hook 中
// 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 接口
// Always define props interface, even if simple
interface ItemCardProps {
item: Item;
onPress: (id: string) => void;
}
export function ItemCard({ item, onPress }: ItemCardProps): JSX.Element {
...
}
状态管理
优先使用本地状态
// Start with useState, escalate only when needed
const [value, setValue] = useState('');
使用 Zustand 管理全局状态(如有需要)
// 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 管理服务端状态
// 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'] });
},
});
}
测试
使用 React Native Testing Library 测试组件
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 测试
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.select
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,
},
}),
});
对复杂差异使用独立文件
Component/
├── Component.tsx # Shared logic
├── Component.ios.tsx # iOS-specific
├── Component.android.tsx # Android-specific
└── index.ts
React Native 反模式
- ❌ 内联样式——使用 StyleSheet.create
- ❌ 在渲染过程中编写逻辑——将其提取到 Hook 中
- ❌ 组件深层嵌套——扁平化层级结构
- ❌ 在 props 中使用匿名函数——改用 useCallback
- ❌ 在列表中使用索引作为 key——使用稳定的 ID
- ❌ 直接修改状态——始终使用 setter
- ❌ 混合业务逻辑与 UI——保持 core/ 纯粹
- ❌ 忽略 TypeScript 错误——修复它们
- ❌ 组件过大——拆分为更小的部分
评论
支持 Markdown