前回、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を使ったりするコードはほとんどクラスコンポーネントなので、まあ普通はクラスコンポーネントの表記にしておくのが無難そう。