用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/UitbreidenOS/UitKit --skill react-native命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
Guidelines and instructions for Agent execution state rollback rules
Guidelines and instructions for Agent execution step counters limits
Guidelines and instructions for Agent execution timeout limits setups
基于 SOC 职业分类
| name | react-native |
| description | - Building React Native apps with Expo managed or bare workflow |
Managed workflow — Expo manages the native layer. No ios/ or android/ directories in the repo. Native functionality comes from Expo SDK modules and config plugins.
Choose managed when:
npx expo start)Bare workflow — React Native project with full native code exposed. Generated via npx expo eject or npx create-expo-app --template bare.
Choose bare when:
Podfile, Gradle, or AndroidManifest.xml is neededIn managed workflow, use config plugins in app.json/app.config.js to modify native code at build time without ejecting:
// app.config.js
export default {
expo: {
name: "MyApp",
plugins: [
["expo-camera", { cameraPermission: "Allow MyApp to use the camera." }],
["expo-location", { locationWhenInUsePermission: "Used for delivery tracking." }],
"./plugins/withCustomAndroidManifest.js", // custom config plugin
],
},
};
// navigation/RootNavigator.tsx
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const Stack = createNativeStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator<TabParamList>();
function TabNavigator() {
return (
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Orders" component={OrdersScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
export function RootNavigator() {
const { isSignedIn } = useAuth();
(
)}
</.>
</>
);
}
Type the param list for type-safe navigation:
// navigation/types.ts
export type RootStackParamList = {
Tabs: NavigatorScreenParams<TabParamList>;
OrderDetail: { orderId: string };
Settings: undefined;
};
export type TabParamList = {
Home: undefined;
Orders: undefined;
Profile: undefined;
};
// In a screen component
const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList, "OrderDetail">>();
navigation.navigate("OrderDetail", { orderId: "42" });
All animation worklets run on the UI thread, not the JS thread. Never access React state or refs directly inside worklets.
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withTiming,
interpolate,
Extrapolation,
} from "react-native-reanimated";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
function SwipeableCard() {
const translateX = useSharedValue(0);
const opacity = useSharedValue(1);
const panGesture = Gesture.Pan()
.onUpdate((e) => {
translateX.value = e.translationX;
opacity.value = interpolate(
Math.abs(e.translationX),
[0, 150],
[1, 0.5],
Extrapolation.CLAMP,
);
})
.onEnd(() => {
if (Math.abs(translateX.value) > 100) {
translateX.value = withTiming(translateX.value > 0 ? : -);
opacity. = ();
} {
translateX. = ();
opacity. = ();
}
});
animatedStyle = ( ({
: [{ : translateX. }],
: opacity.,
}));
(
);
}
Use runOnJS(fn)(args) inside worklets when you must call a JS function (e.g., updating React state after animation completes).
| Module | Purpose |
|---|---|
expo-camera | Camera access with useCameraPermissions hook |
expo-location | GPS / geolocation with requestForegroundPermissionsAsync |
expo-notifications | Push notifications, local notifications, badges |
expo-secure-store | Keychain/Keystore for secrets and tokens |
expo-file-system | Read/write to device filesystem |
expo-image-picker | Gallery and camera photo/video selection |
expo-av | Audio and video playback |
expo-sqlite | SQLite database |
expo-haptics | Haptic feedback (iOS Taptic Engine + Android vibration) |
expo-constants | App version, device info, Expo config at runtime |
{
"cli": {
"version": ">= 10.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"ios": { "simulator": true },
"env": {
"APP_ENV": "development",
"API_URL": "http://localhost:3000"
}
},
"preview": {
"distribution": "internal",
"channel": "preview",
"env"
Build commands:
eas build --platform ios --profile development
eas build --platform android --profile preview
eas build --platform all --profile production
eas submit --platform ios --profile production
EAS Update delivers JavaScript and asset changes without a store release. Native code changes always require a new build.
# Publish an update to the preview channel
eas update --channel preview --message "Fix cart total display"
# Publish to production
eas update --channel production --message "v2.1.1 hotfix"
In app.json, configure the update runtime version policy:
{
"expo": {
"runtimeVersion": {
"policy": "sdkVersion"
},
"updates": {
"url": "https://u.expo.dev/<project-id>",
"fallbackToCacheTimeout": 3000
}
}
}
sdkVersion policy: updates are compatible as long as the Expo SDK version is the same. Use nativeVersion policy for tighter control — only compatible when both SDK and native code version match.
For critical updates, force an immediate reload:
import * as Updates from "expo-updates";
async function checkForUpdate() {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
}
A feed screen with pull-to-refresh using Reanimated and EAS Update:
// screens/FeedScreen.tsx
import Animated, { useAnimatedScrollHandler, useSharedValue } from "react-native-reanimated";
import { FlatList, RefreshControl } from "react-native";
import { useQuery } from "@tanstack/react-query";
export function FeedScreen() {
const scrollY = useSharedValue(0);
const [refreshing, setRefreshing] = React.useState(false);
const { data, refetch } = useQuery({
queryKey: ["feed"],
queryFn: () => fetchFeed(),
});
const onRefresh = async () => {
setRefreshing(true);
await refetch();
setRefreshing(false);
};
const scrollHandler = useAnimatedScrollHandler((e) => {
scrollY.value = e.contentOffset.y;
});
return (
item.id}
renderItem={({ item }) => }
onScroll={scrollHandler}
scrollEventThrottle={16}
refreshControl={
}
/>
);
}
Deploy update after fixing a feed bug:
eas update --channel production --message "Fix feed infinite scroll crash"