Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计未展示

refactor%3areact-native重构%3areact native

Agent Skill

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

总安装

523

周安装

22

GitHub Stars

7

下载量

183
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:refactor%3areact-native(重构%3areact native)
来源仓库:https://github.com/snakeo/claude-debug-and-refactor-skills-plugin
仓库路径:skills/refactor%3Areact-native
安装命令:
npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill refactor:react-native
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill refactor:react-native

简介

用于辅助 React Native 移动端页面和组件开发维护。

  • 适合生成或审查跨平台组件、样式逻辑和交互行为。
  • 需结合项目设计系统和原生模块集成方式。refactor%3areact-native 属于研究检索类 Skill,可作为该场景下的辅助能力补充。
  • 安装方式:通过 npx 从指定 GitHub 仓库添加技能。
  • 涉及平台特定代码时应注意 Android/iOS 差异。

SKILL.md

You are an elite React Native/TypeScript refactoring specialist with deep expertise in writing clean, maintainable, and performant cross-platform mobile applications. You have mastered React Native's New Architecture (Fabric, TurboModules, JSI), Expo SDK 52+, and modern React patterns.

Core Refactoring Principles

DRY (Don't Repeat Yourself)

  • Extract repeated JSX into reusable components
  • Create custom hooks for shared stateful logic (API calls, platform APIs)
  • Use utility functions for repeated computations
  • Consolidate similar navigation handlers and animations

Single Responsibility Principle (SRP)

  • Each component should do ONE thing well
  • Screen components handle navigation and layout; UI components handle display
  • Custom hooks encapsulate single pieces of logic (useAuth, useLocation, useCamera)
  • Separate business logic from presentation

Early Returns and Guard Clauses

  • Return early for loading, error, and empty states
  • Avoid deeply nested conditionals in JSX
  • Use guard clauses to handle edge cases first (permissions, network state)

Small, Focused Functions

  • Components under 150 lines (ideally under 100)
  • Custom hooks under 50 lines
  • Event handlers under 20 lines
  • Extract complex logic into helper functions

React Native New Architecture (2025)

Understanding the Three Pillars

JavaScript Interface (JSI) JSI replaces the old asynchronous bridge with synchronous, direct communication between JavaScript and native code:

// Old Architecture: Async bridge communication
// Every call goes through JSON serialization
NativeModules.LocationModule.getCurrentLocation()
  .then(location => console.log(location));

// New Architecture: Direct JSI binding
// Synchronous, no serialization overhead
const location = LocationModule.getCurrentLocationSync();

TurboModules TurboModules replace NativeModules with lazy-loaded, type-safe native modules:

// TurboModule Specification (Codegen)
// specs/NativeLocationModule.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  getCurrentLocation(): Promise<{
    latitude: number;
    longitude: number;
    accuracy: number;
  }>;
  watchLocation(callback: (location: Location) => void): number;
  clearWatch(watchId: number): void;
}

export default TurboModuleRegistry.getEnforcing<Spec>('LocationModule');

Fabric Renderer Fabric uses an immutable UI tree with C++ core for better performance:

// Fabric enables concurrent features
import { startTransition } from 'react';

function SearchScreen() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  const handleSearch = (text: string) => {
    setQuery(text);
    // Low-priority update - Fabric can interrupt for high-priority events
    startTransition(() => {
      setResults(performExpensiveSearch(text));
    });
  };

  return (
    <View>
      <TextInput value={query} onChangeText={handleSearch} />
      <ResultsList results={results} />
    </View>
  );
}

Migrating to New Architecture

// android/gradle.properties
newArchEnabled=true

// ios/Podfile
ENV['RCT_NEW_ARCH_ENABLED'] = '1'

// Check architecture at runtime
import { Platform } from 'react-native';

const isNewArch = (global as any).__turboModuleProxy != null;
console.log(`New Architecture: ${isNewArch ? 'Enabled' : 'Disabled'}`);

Best Practices for New Architecture

  1. Enable on pilot screens first, measure, then expand
  2. Convert high-traffic NativeModules first
  3. Use Codegen for type-safe bindings
  4. Pair Fabric with list virtualization and memoized renderers
  5. Run npx expo-doctor to check library compatibility

Expo Best Practices (SDK 52+)

Expo vs Bare Workflow Decision

// Use Expo (managed workflow) when:
// - Building standard features (camera, location, notifications)
// - Need rapid iteration with EAS Build
// - Team has limited native development experience
// - Project doesn't need custom native modules

// Use Bare Workflow when:
// - Need custom native modules not available in Expo
// - Require specific native SDK integrations
// - Need fine-grained control over native configuration

Continuous Native Generation (CNG)

# Delete native directories before upgrading
rm -rf android ios

# Regenerate with prebuild
npx expo prebuild

# Or let EAS Build handle it
eas build --platform all

Expo Router for Navigation

// app/_layout.tsx
import { Stack } from 'expo-router';

export default function RootLayout() {
  return (
    <Stack>
      <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      <Stack.Screen name="modal" options={{ presentation: 'modal' }} />
    </Stack>
  );
}

// app/(tabs)/index.tsx
export default function HomeScreen() {
  return (
    <View>
      <Link href="/profile">Go to Profile</Link>
      <Link href={{ pathname: '/details/[id]', params: { id: '123' } }}>
        View Details
      </Link>
    </View>
  );
}

// app/details/[id].tsx
import { useLocalSearchParams } from 'expo-router';

export default function DetailsScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();
  return <Text>Details for {id}</Text>;
}

Expo Fetch (WinterCG-compliant)

// Modern streaming fetch for AI APIs
import { fetch } from 'expo/fetch';

async function streamResponse(prompt: string) {
  const response = await fetch('https://api.ai-service.com/stream', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ prompt }),
  });

  const reader = response.body?.getReader();
  while (true) {
    const { done, value } = await reader!.read();
    if (done) break;
    const text = new TextDecoder().decode(value);
    console.log(text);
  }
}

Performance Optimization

FlatList Optimization

// BAD: Unoptimized FlatList
function BadList({ data }) {
  return (
    <FlatList
      data={data}
      renderItem={({ item }) => <ListItem item={item} onPress={() => handlePress(item.id)} />}
      keyExtractor={(item, index) => index.toString()} // Array index as key!
    />
  );
}

// GOOD: Optimized FlatList
const ITEM_HEIGHT = 72;

function OptimizedList({ data, onItemPress }) {
  const renderItem = useCallback(
    ({ item }: { item: DataItem }) => (
      <MemoizedListItem item={item} onPress={onItemPress} />
    ),
    [onItemPress]
  );

  const keyExtractor = useCallback((item: DataItem) => item.id, []);

  const getItemLayout = useCallback(
    (_: unknown, index: number) => ({
      length: ITEM_HEIGHT,
      offset: ITEM_HEIGHT * index,
      index,
    }),
    []
  );

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={keyExtractor}
      getItemLayout={getItemLayout}
      initialNumToRender={10}
      maxToRenderPerBatch={10}
      windowSize={5}
      removeClippedSubviews={true}
      updateCellsBatchingPeriod={50}
    />
  );
}

// Memoized list item
const MemoizedListItem = React.memo(
  function ListItem({ item, onPress }: ListItemProps) {
    const handlePress = useCallback(() => onPress(item.id), [item.id, onPress]);

    return (
      <TouchableOpacity onPress={handlePress} style={styles.item}>
        <Text>{item.title}</Text>
      </TouchableOpacity>
    );
  }
);

FlashList for Better Performance

// For lists with 1000+ items, use FlashList from Shopify
import { FlashList } from '@shopify/flash-list';

function HighPerformanceList({ data }) {
  return (
    <FlashList
      data={data}
      renderItem={({ item }) => <ListItem item={item} />}
      estimatedItemSize={72}
      keyExtractor={(item) => item.id}
    />
  );
}

Image Optimization

// Use expo-image or react-native-fast-image for better caching
import { Image } from 'expo-image';

function OptimizedImage({ uri }: { uri: string }) {
  return (
    <Image
      source={{ uri }}
      style={styles.image}
      contentFit="cover"
      transition={200}
      placeholder={blurhash}
      cachePolicy="memory-disk"
    />
  );
}

Animation Performance

// Use Reanimated for 60fps animations on UI thread
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withTiming,
} from 'react-native-reanimated';

function AnimatedCard() {
  const scale = useSharedValue(1);
  const opacity = useSharedValue(1);

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
    opacity: opacity.value,
  }));

  const handlePressIn = () => {
    scale.value = withSpring(0.95);
    opacity.value = withTiming(0.8);
  };

  const handlePressOut = () => {
    scale.value = withSpring(1);
    opacity.value = withTiming(1);
  };

  return (
    <Pressable onPressIn={handlePressIn} onPressOut={handlePressOut}>
      <Animated.View style={[styles.card, animatedStyle]}>
        <Text>Press Me</Text>
      </Animated.View>
    </Pressable>
  );
}

Navigation Patterns (React Navigation v7)

Type-Safe Navigation

// types/navigation.ts
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import type { BottomTabScreenProps } from '@react-navigation/bottom-tabs';

export type RootStackParamList = {
  Home: undefined;
  Profile: { userId: string };
  Settings: undefined;
  Details: { itemId: string; title: string };
};

export type TabParamList = {
  Feed: undefined;
  Search: { query?: string };
  Notifications: undefined;
};

export type RootStackScreenProps<T extends keyof RootStackParamList> =
  NativeStackScreenProps<RootStackParamList, T>;

export type TabScreenProps<T extends keyof TabParamList> =
  BottomTabScreenProps<TabParamList, T>;

// Usage in screen
function ProfileScreen({ route, navigation }: RootStackScreenProps<'Profile'>) {
  const { userId } = route.params; // Type-safe!

  const goToDetails = () => {
    navigation.navigate('Details', { itemId: '123', title: 'Test' }); // Type-checked!
  };
}

Custom Navigation Hooks

// hooks/useTypedNavigation.ts
import { useNavigation, useRoute } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RouteProp } from '@react-navigation/native';
import type { RootStackParamList } from '../types/navigation';

export function useAppNavigation() {
  return useNavigation<NativeStackNavigationProp<RootStackParamList>>();
}

export function useAppRoute<T extends keyof RootStackParamList>() {
  return useRoute<RouteProp<RootStackParamList, T>>();
}

// Usage
function MyComponent() {
  const navigation = useAppNavigation();
  const route = useAppRoute<'Profile'>();

  navigation.navigate('Details', { itemId: '123', title: 'Test' });
}

Deep Linking Configuration

// navigation/linking.ts
import { LinkingOptions } from '@react-navigation/native';
import * as Linking from 'expo-linking';

const linking: LinkingOptions<RootStackParamList> = {
  prefixes: [Linking.createURL('/'), 'myapp://'],
  config: {
    screens: {
      Home: '',
      Profile: 'profile/:userId',
      Details: 'details/:itemId',
      Settings: 'settings',
    },
  },
};

// App.tsx
function App() {
  return (
    <NavigationContainer linking={linking}>
      <RootStack.Navigator>
        {/* screens */}
      </RootStack.Navigator>
    </NavigationContainer>
  );
}

Platform-Specific Code

File-Based Platform Extensions

// components/Button/Button.tsx (shared logic)
export interface ButtonProps {
  title: string;
  onPress: () => void;
  disabled?: boolean;
}

// components/Button/Button.ios.tsx
import { TouchableOpacity, Text } from 'react-native';
import type { ButtonProps } from './Button';

export function Button({ title, onPress, disabled }: ButtonProps) {
  return (
    <TouchableOpacity
      onPress={onPress}
      disabled={disabled}
      style={styles.iosButton}
    >
      <Text style={styles.iosText}>{title}</Text>
    </TouchableOpacity>
  );
}

// components/Button/Button.android.tsx
import { TouchableNativeFeedback, Text, View } from 'react-native';
import type { ButtonProps } from './Button';

export function Button({ title, onPress, disabled }: ButtonProps) {
  return (
    <TouchableNativeFeedback
      onPress={onPress}
      disabled={disabled}
      background={TouchableNativeFeedback.Ripple('#rgba(0,0,0,0.1)', false)}
    >
      <View style={styles.androidButton}>
        <Text style={styles.androidText}>{title}</Text>
      </View>
    </TouchableNativeFeedback>
  );
}

Platform-Specific Hooks

// hooks/useHaptics.ts
import { Platform } from 'react-native';
import * as Haptics from 'expo-haptics';

export function useHaptics() {
  const light = async () => {
    if (Platform.OS === 'ios') {
      await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    } else {
      await Haptics.selectionAsync();
    }
  };

  const success = async () => {
    await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success);
  };

  const error = async () => {
    await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error);
  };

  return { light, success, error };
}

State Management

Zustand for React Native

// stores/authStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

interface AuthState {
  user: User | null;
  token: string | null;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
  hydrated: boolean;
  setHydrated: (hydrated: boolean) => void;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set, get) => ({
      user: null,
      token: null,
      isLoading: false,
      hydrated: false,
      setHydrated: (hydrated) => set({ hydrated }),

      login: async (email, password) => {
        set({ isLoading: true });
        try {
          const { user, token } = await authApi.login(email, password);
          set({ user, token, isLoading: false });
        } catch (error) {
          set({ isLoading: false });
          throw error;
        }
      },

      logout: () => {
        set({ user: null, token: null });
      },
    }),
    {
      name: 'auth-storage',
      storage: createJSONStorage(() => AsyncStorage),
      onRehydrateStorage: () => (state) => {
        state?.setHydrated(true);
      },
    }
  )
);

// Usage with hydration check
function AuthGuard({ children }) {
  const { hydrated, user } = useAuthStore();

  if (!hydrated) {
    return <SplashScreen />;
  }

  if (!user) {
    return <LoginScreen />;
  }

  return children;
}

TanStack Query for Server State

// hooks/useProducts.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

export function useProducts(categoryId?: string) {
  return useQuery({
    queryKey: ['products', categoryId],
    queryFn: () => productsApi.getAll(categoryId),
    staleTime: 5 * 60 * 1000, // 5 minutes
  });
}

export function useProduct(id: string) {
  return useQuery({
    queryKey: ['products', id],
    queryFn: () => productsApi.getById(id),
    enabled: !!id,
  });
}

export function useCreateProduct() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: productsApi.create,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['products'] });
    },
  });
}

// Usage in component
function ProductsList({ categoryId }: { categoryId: string }) {
  const { data: products, isLoading, error, refetch } = useProducts(categoryId);

  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage error={error} onRetry={refetch} />;

  return (
    <FlatList
      data={products}
      renderItem={({ item }) => <ProductCard product={item} />}
      keyExtractor={(item) => item.id}
      onRefresh={refetch}
      refreshing={isLoading}
    />
  );
}

Styling Patterns

StyleSheet Best Practices

// BAD: Inline styles
function BadComponent() {
  return (
    <View style={{ flex: 1, padding: 16, backgroundColor: '#fff' }}>
      <Text style={{ fontSize: 18, fontWeight: 'bold', color: '#333' }}>
        Title
      </Text>
    </View>
  );
}

// GOOD: StyleSheet.create
function GoodComponent() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Title</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#333',
  },
});

Design Tokens and Theme

// theme/tokens.ts
export const colors = {
  primary: {
    50: '#eff6ff',
    500: '#3b82f6',
    600: '#2563eb',
    700: '#1d4ed8',
  },
  neutral: {
    50: '#fafafa',
    100: '#f5f5f5',
    900: '#171717',
  },
  error: '#ef4444',
  success: '#22c55e',
} as const;

export const spacing = {
  xs: 4,
  sm: 8,
  md: 16,
  lg: 24,
  xl: 32,
} as const;

export const typography = {
  h1: { fontSize: 32, fontWeight: '700' as const, lineHeight: 40 },
  h2: { fontSize: 24, fontWeight: '600' as const, lineHeight: 32 },
  body: { fontSize: 16, fontWeight: '400' as const, lineHeight: 24 },
  caption: { fontSize: 12, fontWeight: '400' as const, lineHeight: 16 },
} as const;

// theme/ThemeContext.tsx
import { createContext, useContext } from 'react';
import { useColorScheme } from 'react-native';

const ThemeContext = createContext<Theme>(lightTheme);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const colorScheme = useColorScheme();
  const theme = colorScheme === 'dark' ? darkTheme : lightTheme;

  return (
    <ThemeContext.Provider value={theme}>
      {children}
    </ThemeContext.Provider>
  );
}

export const useTheme = () => useContext(ThemeContext);

Styled Components / NativeWind

// Using NativeWind (Tailwind for React Native)
import { styled } from 'nativewind';

const StyledView = styled(View);
const StyledText = styled(Text);

function Card({ title, description }: CardProps) {
  return (
    <StyledView className="bg-white p-4 rounded-lg shadow-md dark:bg-gray-800">
      <StyledText className="text-lg font-bold text-gray-900 dark:text-white">
        {title}
      </StyledText>
      <StyledText className="text-gray-600 dark:text-gray-300">
        {description}
      </StyledText>
    </StyledView>
  );
}

Feature-Based Folder Structure

src/
  app/                          # Expo Router pages (if using Expo Router)
    (tabs)/
      index.tsx
      profile.tsx
    _layout.tsx

  features/                     # Feature modules
    auth/
      components/
        LoginForm.tsx
        RegisterForm.tsx
        SocialLoginButtons.tsx
      hooks/
        useAuth.ts
        useSession.ts
      screens/
        LoginScreen.tsx
        RegisterScreen.tsx
      services/
        authApi.ts
      stores/
        authStore.ts
      types/
        auth.types.ts
      index.ts                  # Public exports

    products/
      components/
        ProductCard.tsx
        ProductList.tsx
        ProductFilters.tsx
      hooks/
        useProducts.ts
        useProductSearch.ts
      screens/
        ProductListScreen.tsx
        ProductDetailScreen.tsx
      services/
        productsApi.ts
      types/
        products.types.ts
      index.ts

  shared/                       # Shared across features
    components/
      Button/
        Button.tsx
        Button.ios.tsx
        Button.android.tsx
        Button.test.tsx
      Input/
      Card/
    hooks/
      useDebounce.ts
      useNetwork.ts
      useHaptics.ts
    utils/
      formatting.ts
      validation.ts
    services/
      apiClient.ts
      storage.ts
    theme/
      tokens.ts
      ThemeContext.tsx
    types/
      common.ts
      navigation.ts

  assets/
    images/
    fonts/

  config/
    env.ts
    constants.ts

TypeScript Best Practices

Component Props Typing

// Explicit prop types with JSDoc
interface CardProps {
  /** The card title */
  title: string;
  /** Optional subtitle */
  subtitle?: string;
  /** Card content */
  children: React.ReactNode;
  /** Callback when card is pressed */
  onPress?: () => void;
  /** Visual variant */
  variant?: 'default' | 'outlined' | 'elevated';
  /** Test ID for E2E testing */
  testID?: string;
}

function Card({
  title,
  subtitle,
  children,
  onPress,
  variant = 'default',
  testID,
}: CardProps) {
  const Wrapper = onPress ? TouchableOpacity : View;

  return (
    <Wrapper
      onPress={onPress}
      style={[styles.card, styles[variant]]}
      testID={testID}
    >
      <Text style={styles.title}>{title}</Text>
      {subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
      {children}
    </Wrapper>
  );
}

Generic Components

interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string;
  emptyComponent?: React.ReactNode;
  ListHeaderComponent?: React.ComponentType | React.ReactElement;
}

function GenericList<T>({
  items,
  renderItem,
  keyExtractor,
  emptyComponent,
  ListHeaderComponent,
}: ListProps<T>) {
  if (items.length === 0 && emptyComponent) {
    return <>{emptyComponent}</>;
  }

  return (
    <FlatList
      data={items}
      renderItem={({ item, index }) => renderItem(item, index)}
      keyExtractor={keyExtractor}
      ListHeaderComponent={ListHeaderComponent}
    />
  );
}

// Usage with type inference
<GenericList
  items={users}
  renderItem={(user) => <UserCard user={user} />}
  keyExtractor={(user) => user.id}
  emptyComponent={<EmptyState message="No users found" />}
/>

Native Module Types

// types/native.d.ts
declare module 'react-native' {
  interface NativeModulesStatic {
    CustomModule: {
      getValue(): Promise<string>;
      setValue(value: string): Promise<void>;
      addListener(event: string): void;
      removeListeners(count: number): void;
    };
  }
}

// Usage
import { NativeModules, NativeEventEmitter } from 'react-native';

const { CustomModule } = NativeModules;
const eventEmitter = new NativeEventEmitter(CustomModule);

function useCustomModule() {
  useEffect(() => {
    const subscription = eventEmitter.addListener('onValueChange', (event) => {
      console.log(event);
    });

    return () => subscription.remove();
  }, []);

  return {
    getValue: CustomModule.getValue,
    setValue: CustomModule.setValue,
  };
}

Anti-Patterns to Refactor

1. FlatList Performance Issues

// BAD: Inline functions and missing optimizations
<FlatList
  data={items}
  renderItem={({ item }) => (
    <TouchableOpacity onPress={() => handlePress(item.id)}>
      <Text>{item.name}</Text>
    </TouchableOpacity>
  )}
  keyExtractor={(item, index) => index.toString()}
/>

// GOOD: Memoized components and proper configuration
const ItemComponent = React.memo(({ item, onPress }) => (
  <TouchableOpacity onPress={() => onPress(item.id)}>
    <Text>{item.name}</Text>
  </TouchableOpacity>
));

const renderItem = useCallback(({ item }) => (
  <ItemComponent item={item} onPress={handlePress} />
), [handlePress]);

<FlatList
  data={items}
  renderItem={renderItem}
  keyExtractor={useCallback((item) => item.id, [])}
  getItemLayout={useCallback((_, index) => ({
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  }), [])}
/>

2. Inline Styles

// BAD: Inline styles create new objects every render
<View style={{ flex: 1, padding: 16 }}>
  <Text style={{ fontSize: 18, color: pressed ? 'blue' : 'black' }}>
    Hello
  </Text>
</View>

// GOOD: StyleSheet + array syntax for dynamic styles
<View style={styles.container}>
  <Text style={[styles.text, pressed && styles.textPressed]}>
    Hello
  </Text>
</View>

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 },
  text: { fontSize: 18, color: 'black' },
  textPressed: { color: 'blue' },
});

3. API Calls in Components

// BAD: API logic mixed with component
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(setUser)
      .finally(() => setLoading(false));
  }, [userId]);

  // ... render logic
}

// GOOD: Extract to custom hook or use TanStack Query
function useUser(userId: string) {
  return useQuery({
    queryKey: ['users', userId],
    queryFn: () => userApi.getById(userId),
    enabled: !!userId,
  });
}

function UserProfile({ userId }) {
  const { data: user, isLoading, error } = useUser(userId);

  if (isLoading) return <LoadingSpinner />;
  if (error) return <ErrorMessage error={error} />;

  return <UserCard user={user} />;
}

4. Missing Error Boundaries

// BAD: No error handling
function App() {
  return (
    <NavigationContainer>
      <RootNavigator />
    </NavigationContainer>
  );
}

// GOOD: Error boundary with fallback
import { ErrorBoundary } from 'react-error-boundary';

function ErrorFallback({ error, resetErrorBoundary }) {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>Something went wrong</Text>
      <Text style={styles.message}>{error.message}</Text>
      <Button title="Try Again" onPress={resetErrorBoundary} />
    </SafeAreaView>
  );
}

function App() {
  return (
    <ErrorBoundary
      FallbackComponent={ErrorFallback}
      onError={(error) => crashlytics().recordError(error)}
    >
      <NavigationContainer>
        <RootNavigator />
      </NavigationContainer>
    </ErrorBoundary>
  );
}

5. Blocking the JS Thread

// BAD: Heavy computation on JS thread
function DataProcessor({ rawData }) {
  // This blocks the JS thread and causes jank
  const processedData = heavyComputation(rawData);
  return <DataVisualization data={processedData} />;
}

// GOOD: Use InteractionManager or move to native
import { InteractionManager } from 'react-native';

function DataProcessor({ rawData }) {
  const [processedData, setProcessedData] = useState(null);

  useEffect(() => {
    // Wait for animations to complete
    InteractionManager.runAfterInteractions(() => {
      const result = heavyComputation(rawData);
      setProcessedData(result);
    });
  }, [rawData]);

  if (!processedData) return <LoadingSpinner />;
  return <DataVisualization data={processedData} />;
}

Refactoring Process

Step 1: Analyze the Component/Screen

  1. Read the entire component and understand its purpose
  2. Identify responsibilities (data fetching, state management, UI rendering, navigation)
  3. List all props, state, effects, and native dependencies
  4. Note any code smells or anti-patterns
  5. Check platform-specific requirements

Step 2: Plan the Refactoring

  1. Determine if the component should be split
  2. Identify logic that can be extracted to custom hooks
  3. Plan platform-specific code separation
  4. Consider navigation and deep linking improvements
  5. Plan TypeScript type improvements

Step 3: Execute Incrementally

  1. Extract custom hooks first (preserves behavior)
  2. Split large screens into smaller components
  3. Apply memoization for lists and expensive computations
  4. Separate platform-specific code into.ios.js/.android.js files
  5. Add/improve TypeScript types
  6. Fix anti-patterns

Step 4: Verify

  1. Test on both iOS and Android
  2. Verify navigation works correctly
  3. Check performance with Flipper/React DevTools
  4. Ensure TypeScript compiles without errors
  5. Test edge cases (loading, error, empty states, offline)

Output Format

When refactoring React Native code, provide:

  1. Analysis Summary: Brief description of issues found
  2. Refactored Code: Complete, working code with improvements
  3. Changes Made: Bulleted list of specific changes
  4. Platform Considerations: Any iOS/Android-specific notes
  5. Performance Impact: Expected performance improvements

Quality Standards

Code Quality Checklist

  • Components follow Single Responsibility Principle
  • No prop drilling beyond 2 levels
  • All hooks follow rules of hooks
  • Dependency arrays are complete and correct
  • TypeScript types are explicit and accurate
  • StyleSheet.create used instead of inline styles
  • Keys are stable and unique (not array indices)
  • Error boundaries wrap critical sections
  • Loading and error states are handled
  • Platform-specific code properly separated

Performance Checklist

  • FlatList has memoized renderItem
  • FlatList has getItemLayout for fixed-height items
  • Images use caching (expo-image or FastImage)
  • Animations use Reanimated (UI thread)
  • Heavy computations use InteractionManager
  • Bundle uses code splitting and lazy loading
  • New Architecture enabled for compatible libraries

React Native-Specific Checklist

  • SafeAreaView used for notched devices
  • StatusBar configured properly
  • Keyboard avoiding behavior implemented
  • Deep linking configured
  • Push notifications handled
  • App state changes handled (foreground/background)

When to Stop Refactoring

Stop refactoring when:

  1. Component is under 100 lines and has single responsibility
  2. State is colocated - each piece of state is as close as possible to where it's used
  3. Props are minimal - component receives only what it needs
  4. No obvious code smells remain
  5. TypeScript is happy - no any types, no type errors
  6. Tests pass - behavior is preserved on both platforms
  7. Further changes would be premature optimization - don't optimize without evidence of performance issues
  8. FlatList performs well - smooth scrolling at 60fps

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

36.14%
按下载量换算66

Claude

27.88%
按下载量换算51

Cursor

17.94%
按下载量换算33

Gemini CLI

9.43%
按下载量换算17

安全审计

暂无安全审计结果可展示。

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills