Rorkで作ったアプリを実機テストしてみたら「なんかもっさりしている」「画面の切り替えが遅い」「一定の操作でフリーズする」という経験はありませんか?
AIがコードを生成してくれるRorkの便利さの一方、パフォーマンス問題は自動では解決されません。ここではRorkアプリが重くなる代表的な原因を診断フローとともに整理し、具体的な修正方法をコード例つきで解説します。
よくあるパフォーマンス問題の症状チェックリスト
まず自分のアプリがどのパターンに当てはまるか確認しましょう。
スクロールがカクつく・引っかかる感じがする場合はFlatList・ScrollViewの問題、画面遷移のアニメーションがもたつく場合はNavigationの問題、特定のボタンを押したときだけ固まる場合は重い処理がメインスレッドをブロックしている可能性があります。またアプリ起動時間が長い場合は初期レンダリングの問題、長時間使っているとだんだん重くなる場合はメモリリーク、画像・動画の表示が遅い・チラつく場合は画像最適化の問題です。
それぞれの症状に対応した解決策を以下で解説します。
問題1:スクロールがカクつく
Rorkで生成されたリスト表示にmap()やScrollViewが使われている場合、データ件数が増えるとパフォーマンスが著しく低下します。
原因
ScrollViewはすべての子コンポーネントを一度にレンダリングするため、100件以上のデータがあると必ずカクつきます。
解決策:FlatListへの置き換え
// 重い実装(件数が増えるとカクつく)
<ScrollView>
{items.map((item) => (
<ItemCard key={item.id} item={item} />
))}
</ScrollView>
// 軽い実装(画面外の要素は仮想化してレンダリングを省略)
<FlatList
data={items}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <ItemCard item={item} />}
initialNumToRender={10}
maxToRenderPerBatch={5}
windowSize={5}
removeClippedSubviews={true}
/>FlatListに切り替えただけで、スクロールのカクつきが解消されるケースが多いです。
ItemCardコンポーネントの最適化
リストアイテムのコンポーネント自体が重い場合はReact.memoでラップします。
const ItemCard = React.memo(({ item }: { item: Item }) => {
return (
<View style={styles.card}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description}>{item.description}</Text>
</View>
);
});
ItemCard.displayName = 'ItemCard';問題2:画面遷移が重い・アニメーションがもたつく
画面遷移時にアニメーションがカクつく場合、重い初期化処理が原因であることが多いです。
原因の診断
const HeavyScreen = () => {
const startTime = useRef(Date.now());
useEffect(() => {
const renderTime = Date.now() - startTime.current;
console.log(`[パフォーマンス] HeavyScreen レンダリング時間: ${renderTime}ms`);
// 100ms超えたら要注意、500ms超えたら問題あり
if (renderTime > 100) {
console.warn('[警告] レンダリングに時間がかかっています:', renderTime, 'ms');
}
}, []);
return <View>...</View>;
};解決策:Lazy Loadingで遷移先の初期化を遅延
import { lazy, Suspense } from 'react';
const HeavyDetailScreen = lazy(() => import('./HeavyDetailScreen'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<HeavyDetailScreen />
</Suspense>
);
}解決策2:画面表示後に非同期でデータ取得
const ProductListScreen = () => {
const [products, setProducts] = useState<Product[]>([]);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const loadProducts = async () => {
try {
const data = await fetchProducts();
setProducts(data);
} catch (error) {
console.error('商品データの取得に失敗:', error);
} finally {
setIsReady(true);
}
};
loadProducts();
}, []);
if (\!isReady) return <SkeletonLoader />;
return <FlatList data={products} renderItem={({ item }) => <ProductItem item={item} />} />;
};問題3:特定の操作でフリーズする
ボタンタップ後に一瞬画面が固まる場合、重い処理がJavaScriptのメインスレッドをブロックしています。
原因の特定と修正
// メインスレッドをブロックする重い処理(問題あり)
const handleCalculate = () => {
const result = bigData.map(item => expensiveCalculation(item));
setResult(result);
};
// 非同期処理でメインスレッドを解放する(推奨)
const handleCalculate = async () => {
setLoading(true);
await new Promise(resolve => requestAnimationFrame(resolve));
try {
const result = await processInChunks(bigData, 100);
setResult(result);
} finally {
setLoading(false);
}
};
// 大量データを小分けに処理するユーティリティ
async function processInChunks<T>(
data: T[],
chunkSize: number
): Promise<T[]> {
const results: T[] = [];
for (let i = 0; i < data.length; i += chunkSize) {
const chunk = data.slice(i, i + chunkSize);
results.push(...chunk);
// 各チャンク処理後にメインスレッドを解放
await new Promise(resolve => setTimeout(resolve, 0));
}
return results;
}問題4:アプリ起動が遅い
起動時間が3秒を超える場合、初期ロードの最適化が必要です。
不要なimportの削除
Rorkが生成するコードには使用していないライブラリのimportが含まれることがあります。
// 未使用のimportがバンドルサイズを増大させる(問題あり)
import { View, Text, TextInput, TouchableOpacity, Modal, Picker, DatePicker } from 'react-native';
// 使用するものだけをimport(推奨)
import { View, Text, TextInput, TouchableOpacity } from 'react-native';スプラッシュスクリーンで起動時間を管理
import * as SplashScreen from 'expo-splash-screen';
SplashScreen.preventAutoHideAsync();
export default function App() {
const [appReady, setAppReady] = useState(false);
useEffect(() => {
const prepare = async () => {
try {
await Font.loadAsync({ 'CustomFont': require('./assets/fonts/custom.ttf') });
await Asset.loadAsync([require('./assets/images/logo.png')]);
await initializeAppData();
} catch (error) {
console.warn('起動処理でエラー:', error);
} finally {
setAppReady(true);
}
};
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appReady) await SplashScreen.hideAsync();
}, [appReady]);
if (\!appReady) return null;
return (
<View style={{ flex: 1 }} onLayout={onLayoutRootView}>
<Navigation />
</View>
);
}問題5:長時間使っているとだんだん重くなる(メモリリーク)
アプリを長時間使うほど遅くなる場合、メモリリークが疑われます。最もよくある原因はuseEffectのクリーンアップ忘れです。
診断と修正
// メモリリークが発生するパターン(クリーンアップなし)
useEffect(() => {
const subscription = eventEmitter.addListener('update', handleUpdate);
const timer = setInterval(fetchLatestData, 5000);
// クリーンアップがない → コンポーネント削除後も処理が継続する
}, []);
// 正しいクリーンアップ(推奨)
useEffect(() => {
const subscription = eventEmitter.addListener('update', handleUpdate);
const timer = setInterval(fetchLatestData, 5000);
return () => {
subscription.remove();
clearInterval(timer);
};
}, []);
// 非同期処理でのメモリリーク対策
useEffect(() => {
let isMounted = true;
fetchData().then(data => {
if (isMounted) setData(data); // マウント済みの場合のみstate更新
});
return () => { isMounted = false; };
}, []);問題6:画像の表示が遅い・チラつく
expo-imageへの移行
import { Image } from 'expo-image';
<Image
source={{ uri: 'https://example.com/product.jpg' }}
style={styles.productImage}
contentFit="cover"
cachePolicy="memory-disk"
placeholder={require('./assets/placeholder.png')}
transition={200}
/>ローカル画像のサイズ最適化にはpngquantが便利です。
# assets/imagesフォルダ内の全PNGを圧縮
find assets/images -name "*.png" -exec pngquant --force --quality=65-80 {} \;Expo DevToolsでパフォーマンスを可視化する
問題の特定には、Expo DevToolsのPerformanceタブが非常に役立ちます。
npx expo start
# ターミナルで 'm' を押してMetro UIを開く
# Performanceタブでフレームレートとメモリ使用量を確認する確認すべき指標は、FPS(フレームレート)は60fpsが理想で30fps以下はユーザーが体感できるカクつきになります。JS Threadは80ms以上かかる処理があれば要注意、UI Threadもブロックされていないか確認が必要です。
パフォーマンス計測の実践的な手法を
全体を振り返って
Rorkアプリのパフォーマンス問題は、以下の優先順位で対処すると効率的です。
スクロールのカクつきはFlatListへの置き換えとReact.memoで即効性があります。フリーズは重い処理を非同期化してメインスレッドを解放することで解決します。起動時間の問題は不要なimportを削除し、スプラッシュスクリーンで体験を改善します。メモリリークはuseEffectのクリーンアップを必ず実装することで防げます。画像の遅延はexpo-imageへの移行とキャッシュポリシーの設定で改善します。
パフォーマンス問題は「あとで直せばいい」と後回しにしがちですが、App Storeのレビューやユーザー離脱率に直結するため、リリース前に必ず実機でチェックする習慣をつけることをおすすめします。
Rorkの技術的なエラー対処全般についてはRorkアプリ ビルドエラー完全対処ガイドも合わせてご参照ください。また、パフォーマンスをさらに向上させる積極的な手法についてはRorkアプリ パフォーマンス最適化もご覧ください。