前回、Firestoreからデータを取得してとりあえずログに表示するところと、3カラムレイアウトを組むところを試したので、今回は取り出したデータを3カラムレイアウトで画像表示してみます。
前回のソースコードで続きを書こうと思ったらなぜか動かなくなってしまった。経緯はメモし忘れたけど、とりあえずエラーメッセージは載っけておく。
Unable to resolve "@expo/vector-icons/Fontisto" from "node_modules/native-base/dist/src/basic/IconNB.js"
Error: Can't find react-native in package.json dependencies Unable to resolve "react-native" from "node_modules/expo/build/launch/registerRootComponent.js" Failed building JavaScript bundle.
Error: Can't find react-native in package.json dependencies
イマイチ解決の糸口が見つけられなかったので、一旦プロジェクトを作るところからやりなおすことに。。
$ rm -rf MenstagramRN $ expo init MenstagramRN
前回は素直に従わずにnpmを使ったのですが、今回はexpoが推してくるyarnを使うことにします。
$ yarn start
なにやらシミュレータ上でエラーが出ていて動かない。シミュレータ内のExpoアプリを新しくしろとのこと。
The experience you requested requires a newer version of the Expo Client app.
下記エントリに記載されている通り、一度シミュレータのExpoアプリをアンインストールする。
https://www.aizulab.com/blog/project-not-shown-in-expo-client-app/
再びyarn startしたらちゃんとブランクのアプリが動いたので、次に進めます(スクショ取り忘れた)。
$ yarn start
ようやく本題に進める。まずはfirebaseのパッケージをインストール。
$ yarn add firebase
前回のコードと一緒だけど一応Firebaseのクライアントを初期化するコードを載っけとく。
firebase.js
import firebase from "firebase";
import 'firebase/firestore';
const firebaseConfig = {
apiKey: "<apiKey>",
authDomain: "<authDomain>",
databaseURL: "<databaseURL>",
projectId: "<projectId>",
storageBucket: "<storageBucket>",
messagingSenderId: "<messagingSenderId>",
appId: "<appId>"
};
const firebaseApp = !firebase.apps.length
? firebase.initializeApp(firebaseConfig)
: firebase.app()
const db = firebaseApp.firestore();
export default db
コンポーネントの実装。TypeScriptにしているけど、まだあんまりTypeScriptの使い方を理解していない。
ちなみに、前回は3カラムレイアウトを実装するのにreact-native-easy-gridを使ったけど、動的データを使って並べるのがやりにくそうだったのでFlatListでの実装に変えた。これはこれで、自分でImageのwidthを計算してるところとかスマートじゃないけど仕方ないのかな。
App.tsx
import React, { Component } from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import { Image, FlatList} from "react-native";
import { Container, Header } from 'native-base';
import db from './firebase'
export default class App extends Component {
state = { ramens: [] };
componentDidMount() {
db.collection("ramens").get().then((querySnapshot) => {
const ramens = querySnapshot.docs.map(doc => doc.data())
this.setState({ ramens: ramens })
});
}
render() {
return (
<Container>
<Header />
<FlatList
data={this.state.ramens}
renderItem={({ item }) => (
<View>
<Image
source={{ uri: item.imageUrl }}
style={styles.imageStyle}
/>
</View>
)}
numColumns={3}
keyExtractor={(item, index) => index.toString()}
/>
</Container>
);
}
}
const windowWidth = Dimensions.get('window').width;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
imageStyle: {
width: windowWidth / 3,
height: windowWidth / 3,
margin: 1,
resizeMode: 'cover',
}
});
よし出来た。

Appの表記について
expo initで生成したときに出来るコードは以下のようになっているが、
export default function App() {
多くのサンプルコードを見ると、以下のようになっているケースのほうが多い。
export default class App extends Component {
完全に理解出来ているわけではないが、前者は関数コンポーネントで、後者はクラスコンポーネントということらしい。
https://ja.reactjs.org/docs/components-and-props.html
シンプルに書くなら関数コンポーネントでも良さそうだけど、stateを使ったりするコードはほとんどクラスコンポーネントなので、まあ普通はクラスコンポーネントの表記にしておくのが無難そう。