このブログについて
- Expoを使いつつ、ReactNative のアプリ開発についてまとめていきます。
 - 必要に応じて、ライブラリなどの紹介します
 - チャプターごとの状態を GitHub で管理するので、必要であればクローンしてください。
 
こんな方にオススメ
- React、TypeScript などについては学んでいる
 - スマホアプリケーションの開発に興味がある
 
前提ツールのバージョン
| ツール | バージョン | 
|---|---|
| node | 22.19.0 | 
| pnpm | 10.15.1 | 
| expo(SDK) | 54 | 
過去の投稿

【1回】Expo・ReactNativeの環境構築
このブログについてExpoを使いつつ、ReactNative のアプリ開発についてまとめていきます。必要に応じて、ライブラリなどの紹介しますチャプターごとの状態を GitHub で管理するので、必要であればクローンしてください。こんな方にオ...

【2回】Expo・ReactNativeのView・Text・TextInput・Buttonの使い方
このブログについて Expoを使いつつ、ReactNative のアプリ開発についてまとめていきます。 必要に応じて、ライブラリなどの紹介します チャプターごとの状態を GitHub で管理するので、必要であればクローンしてください。こんな...
事前準備
ファイル作成
- app/(Home)/blogs.tsx
 
追記・修正
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <Stack> | |
| // ここから | |
| <Stack.Screen | |
| name="blogs" | |
| options={{ title: "ブログ", headerShown: false }} | |
| /> | |
| // ここまで | |
| </Stack> | 
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { useRouter } from "expo-router"; | |
| export default function Home() { | |
| const [email, setEmail] = React.useState(""); | |
| const router = useRouter(); | |
| // ・・・ | |
| return ( | |
| <Button | |
| title="送信" | |
| onPress={() => router.push("/(Home)/blogs")} | |
| /> | |
| // ・・・ | |
| ); | |
| } | 
ScrollViewとFlatList


前回、Viewというタグを紹介しました。Web系ならdivタグに対してoverflow: scrollを設定できますがExpoでは ScrollView や FlatList を用いる必要がある。
コードサンプル
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { | |
| FlatList, | |
| ScrollView, | |
| StyleSheet, | |
| Text, | |
| View, | |
| ViewStyle, | |
| } from "react-native"; | |
| import { SafeAreaView } from "react-native-safe-area-context"; | |
| type BlogItemProps = { | |
| title: string; | |
| content: string; | |
| style?: ViewStyle; | |
| }; | |
| function BlogItem({ title, content, style }: BlogItemProps) { | |
| return ( | |
| <View style={[styles.item, style]}> | |
| <Text style={styles.itemTitle}>{title}</Text> | |
| <Text style={styles.itemContent}>{content}</Text> | |
| </View> | |
| ); | |
| } | |
| export default function Blogs() { | |
| const blogs = [ | |
| { | |
| id: 1, | |
| title: "Blog 1", | |
| content: "Content 1", | |
| }, | |
| // 省略 | |
| ]; | |
| // 横スクロール用のデータ | |
| const horizontalScrollItems = blogs.map((blog) => ( | |
| <BlogItem | |
| key={blog.id} | |
| title={blog.title} | |
| content={blog.content} | |
| style={styles.horizontalScrollItem} | |
| /> | |
| )); | |
| // 縦スクロール用のデータ | |
| const verticalScrollItems = blogs.map((blog) => ( | |
| <BlogItem | |
| key={blog.id} | |
| title={blog.title} | |
| content={blog.content} | |
| style={styles.verticalScrollItem} | |
| /> | |
| )); | |
| // FlatList用のレンダリング関数(横スクロール用) | |
| const renderHorizontalItem = ({ item }: BlogItemProps }) => ( | |
| <BlogItem | |
| title={item.title} | |
| content={item.content} | |
| style={styles.horizontalListItem} | |
| /> | |
| ); | |
| // FlatList用のレンダリング関数(縦スクロール用) | |
| const renderVerticalItem = ({ item }: { item: BlogItemProps}) => ( | |
| <BlogItem | |
| title={item.title} | |
| content={item.content} | |
| style={styles.verticalListItem} | |
| /> | |
| ); | |
| return ( | |
| <SafeAreaView style={styles.safeArea}> | |
| <View style={styles.container}> | |
| {/* 上部: 横スクロールのScrollView */} | |
| <View style={styles.scrollViewContainer}> | |
| <Text style={styles.sectionTitle}>ScrollView(横スクロール)</Text> | |
| <ScrollView | |
| horizontal | |
| showsHorizontalScrollIndicator={false} | |
| style={styles.horizontalScrollView} | |
| contentContainerStyle={styles.horizontalScrollViewContent} | |
| > | |
| {horizontalScrollItems} | |
| </ScrollView> | |
| </View> | |
| {/* 中央: 縦スクロールのScrollView */} | |
| <View style={styles.scrollViewContainer}> | |
| <Text style={styles.sectionTitle}>ScrollView(縦スクロール)</Text> | |
| <ScrollView | |
| showsVerticalScrollIndicator={false} | |
| style={styles.verticalScrollView} | |
| contentContainerStyle={styles.verticalScrollViewContent} | |
| > | |
| {verticalScrollItems} | |
| </ScrollView> | |
| </View> | |
| {/* 下部: 横スクロールのFlatList */} | |
| <View style={styles.listViewContainer}> | |
| <Text style={styles.sectionTitle}>FlatList(横スクロール)</Text> | |
| <FlatList | |
| data={blogs} | |
| renderItem={renderHorizontalItem} | |
| keyExtractor={(item) => item.id.toString()} | |
| horizontal | |
| showsHorizontalScrollIndicator={false} | |
| style={styles.horizontalFlatList} | |
| contentContainerStyle={styles.horizontalFlatListContent} | |
| /> | |
| </View> | |
| {/* 最下部: 縦スクロールのFlatList */} | |
| <View style={styles.listViewContainer}> | |
| <Text style={styles.sectionTitle}>FlatList(縦スクロール)</Text> | |
| <FlatList | |
| data={blogs} | |
| renderItem={renderVerticalItem} | |
| keyExtractor={(item) => item.id.toString()} | |
| showsVerticalScrollIndicator={false} | |
| style={styles.verticalFlatList} | |
| contentContainerStyle={styles.verticalFlatListContent} | |
| /> | |
| </View> | |
| </View> | |
| </SafeAreaView> | |
| ); | |
| } | |
| const styles = StyleSheet.create({ | |
| // 省略 | |
| }); | 
ScrollView



ScrollView · React Native
Component that wraps platform ScrollView while providing integration with touch locking "responder" system.
| Props | 型 | 備考 | 
|---|---|---|
| horizontal | boolean | 横向きスクロールに変更する。 デフォルトは縦向き。 true: 横向き  | 
| showsHorizontalScrollIndicator | boolean | 横向きのスクロールバーの表示・非表示設定 true: 表示 false: 非表示  | 
| showsVerticalScrollIndicator | boolean | 縦向きのスクロールバーの表示・非表示設定 true: 表示 false: 非表示  | 
FlatView



FlatList · React Native
A performant interface for rendering basic, flat lists, supporting the most handy features:
| Props | 型 | 備考 | 必須 | 
|---|---|---|---|
| data | Array | 表示するデータの配列。 | ○ | 
| renderItem | (item, index) => JSX.Element | dataを整形して表示する コンポーネントを定義する。 itemはdataの要素。  | 
○ | 
| keyExtractor | (item) => string | renderItemで描画された | |
| horizontal | boolean | 横向きスクロールに変更する。 デフォルトは縦向き。 true: 横向き  | 
|
| showsHorizontalScrollIndicator | boolean | 横向きのスクロールバーの表示・非表示設定 true: 表示 false: 非表示  | 
|
| showsVerticalScrollIndicator | boolean | 縦向きのスクロールバーの表示・非表示設定 true: 表示 false: 非表示  | 
どっちを使えば良いか
公式的には以下のような記載があります。
ScrollViewrenders all its react child components at once, but this has a performance downside.
Imagine you have a very long list of items you want to display, maybe several screens worth of content. Creating JS components and native views for everything all at once, much of which may not even be shown, will contribute to slow rendering and increased memory usage.
ScrollViewだとリストの内容を1度に描画されるので、量が多いとパフォーマンスが落ちる。
無限スクロールを実装する場合などは FlatView のほうがよい。
他にも FlatView だと、HeaderやFooterなどTable的なことも標準で用意されている。
一方で、データを持たない場合(画面のスクロール)などは ScrollView を使った方が良いと思う。
決まった型があるデータの有無で分けて良さそうな気はする。
まとめ
- ScrollViewとFlatListについて紹介しました
 - データの型が決まっている場合や量が多い場合はFlatListを使う
 - スクリーン全体など型がない場合はScrollViewを使う
 
参考資料
https://docs.expo.dev
Build one JavaScript/TypeScript project that runs natively on all your users' devices.



React Native · Learn once, write anywhere
A framework for building native apps for Android, iOS, and more using React
 

コメント