{"id":18432348,"url":"https://github.com/naturalclar/expo-firebase-chat","last_synced_at":"2025-04-07T18:33:39.471Z","repository":{"id":97449215,"uuid":"374274951","full_name":"Naturalclar/expo-firebase-chat","owner":"Naturalclar","description":"Example implementation of simple chat app with expo and firebase","archived":false,"fork":false,"pushed_at":"2021-06-06T05:22:48.000Z","size":1309,"stargazers_count":11,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-22T22:42:29.627Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Naturalclar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-06T05:22:38.000Z","updated_at":"2023-01-23T23:10:11.000Z","dependencies_parsed_at":"2023-04-07T05:01:08.817Z","dependency_job_id":null,"html_url":"https://github.com/Naturalclar/expo-firebase-chat","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Naturalclar%2Fexpo-firebase-chat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Naturalclar%2Fexpo-firebase-chat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Naturalclar%2Fexpo-firebase-chat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Naturalclar%2Fexpo-firebase-chat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Naturalclar","download_url":"https://codeload.github.com/Naturalclar/expo-firebase-chat/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247707610,"owners_count":20982816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T05:28:18.880Z","updated_at":"2025-04-07T18:33:39.428Z","avatar_url":"https://github.com/Naturalclar.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# expo-firebase-chat-example\n\nこのリポジトリは、[新生活応援！React Native ハンズオン 入門編 #3](https://react-native-meetup.connpass.com/event/213379/)\b のハンズオンでできる最終成果物です。\n\n今回のハンズオンでは以下の目次の沿ってチャットアプリを作成していきます。\n\n今までのハンズオンに参加してなくても最初から最後まで進められるようになっております。\n\n## 目次\n\n- [1. プロジェクトのセットアップ](#1.%20プロジェクトのセットアップ)\n- [2. チャット画面の作成](#2.%20チャット画面の作成)\n- [3. react-navigation のセットアップ](#3.%20react-navigation%20のセットアップ)\n- [4. 登録・ログイン画面の作成](#4.%20ログイン画面の作成)\n- [5. firebase との連携:アカウント登録](#5.%20firebase%20との連携:アカウント登録)\n- [6. firebase との連携:db 接続](#6.%20firebase%20との連携:db%20接続)\n\n## 前提条件\n\nこのハンズオンでは、以下の環境が必要となります。\n\n- node.js v12+\n- yarn\n- firebase を使用するための Google アカウント\n\n## 1. プロジェクトのセットアップ\n\nこの章では、react-native のアプリを作成するために必要なセットアップを行います。\n\nreact-native のアプリを作成するには主に以下の 2 つの方法があります。\n\n- `@react-native-community/cli` を使ったプロジェクトの作成\n- `expo-cli` を使ったプロジェクトの作成\n\n`@react-native-community/cli` は react-native のコミュニティが提供している公式の cli パッケージであり、react-native をそのまま使ってアプリを作成する方法です。\nこちらの方法は、サードパーティが作った機能などを全て使うことができて、自由度が高いです。\nその代わり、アプリのビルドを行うのに多少のネイティブ開発の知識が必要となります。\n\n`expo-cli` は `expo` という react-native の上に立っているフレームワークを使ってアプリを作成する方法です。\nこちらは、ネイティブのビルド処理などを expo が賄ってくれるため、 javascript の知識だけでアプリが開発できるようになっています。\nその代わり、expo では使用できるサードパーティ機能の数が限られており、react-native をそのまま書くよりは柔軟性が低いです。とはいえ、基本的なアプリを作成するのには十分な機能が備わっています。\n\n今回のハンズオンでは、`expo-cli` を使ったプロジェクトを作成していきます。\n\n### i. expo-cli のインストール\n\nまずは、`expo-cli` をインストールします。\n\nこれをインストールすることによって、ターミナル上で `expo` コマンドが使えるようになります。\n\n```\nyarn global add expo-cli\n```\n\n### ii. 新しい expo project の作成\n\nインストールした expo-cli を使って今回のハンズオン用のプロジェクトを作成してみましょう。\n\n下記コマンドで新しく expo のプロジェクトを作成します。\n\n```\nexpo init expo-firebase-chat\n```\n\n`expo init` の後の名前は好きな名前でかまいません。\nこのハンズオンでは、`expo-firebase-chat` というプロジェクト名で進めていきます。\n\nコマンドを叩いたら、以下のような選択肢が出てきます。\nこのハンズオンでは、`blank` を選択します。\n普段から TypeScript を使っているかたは、`blank(TypeScript)` でも問題ありません。\n\n![expo-init commands](./screenshots/expo-init.png)\n\n作成したプロジェクトのディレクトリに移動します\n\n```\ncd expo-firebase-chat\n```\n\n### iii. シミュレーターの立ち上げ\n\nここで、試しに initialize したアプリを立ち上げてみましょう。\n以下のコマンドでアプリを立ち上げることができます。\n\n```\nexpo start\n```\n\nコマンドを叩くと以下の様な画面が出てきます。\n\n![expo-start commands](./screenshots/expo-start.png)\n\n`a` を入力すると Android、`i` を入力すると iOS のシミュレータが立ち上がります。\nお好みの方を立ち上げてみましょう。\n\nこのハンズオンでは iOS simulator を使って進めていきますが、Android でも基本的には同じです。\n\n初回起動時は Expo GO というアプリがインストールされて、その中で今回のプロジェクトが立ち上がります。\n\n以下のような画面が表示されるでしょう。\n\n| iOS                                                                                     |\n| --------------------------------------------------------------------------------------- |\n| \u003cimg src=\"./screenshots/welcome.png\" width=\"320\" alt=\"React Native Hello World Screen\"\u003e |\n\n試しに、`App.js`\b を開き、`\u003cText\u003e` の中身を編集してみましょう。\n編集して保存したら、変更がシミュレータにも反映されます。\n\nこれで、アプリを作成していく準備は完了です。\n\n次の章から、チャットアプリに必要な画面を作っていきます。\n\n## 2. チャット画面の作成\n\nこの章では、簡易的なチャット画面を作成します。\n\nチャット画面の見た目は、 `react-native-gifted-chat` というライブラリを使って作成します。\n\n### i. react-native-gifted-chat のインストール\n\nまずは必要なライブラリをインストールします。\n\n```\nyarn add react-native-gifted-chat\n```\n\nこれで、プロジェクト内で `GiftedChat` のコンポーネントを作成できるようになります。\n\n### ii. チャット画面の作成\n\n`App.js` の中身を消して、以下のように変更しましょう。\n\n```jsx\n// App.js\nimport * as React from \"react\";\nimport { View, StyleSheet, KeyboardAvoidingView } from \"react-native\";\nimport { GiftedChat } from \"react-native-gifted-chat\";\n\nconst styles = StyleSheet.create({\n  container: { flex: 1 },\n});\n\nexport default App () {\n  // 表示するチャットメッセージの配列\n  const [messages, setMessages] = React.useState([]);\n\n  React.useEffect(() =\u003e {\n    setMessages([\n      {\n        _id: 1,\n        text: \"Hello developer\",\n        createdAt: new Date(),\n        user: {\n          _id: 2,\n          name: \"React Native\",\n          avatar: \"https://placeimg.com/140/140/any\",\n        },\n      },\n    ]);\n  }, []);\n\n  const onSend = React.useCallback((messages = []) =\u003e {\n    setMessages((previousMessages) =\u003e\n      GiftedChat.append(previousMessages, messages)\n    );\n  }, []);\n\n  return (\n    \u003cView style={styles.container}\u003e\n      \u003cGiftedChat\n        messages={messages}\n        placeholder=\"メッセージを入力してください...\"\n        onSend={onSend}\n        user={{\n          _id: 1,\n          name: \"Me\",\n          avatar: \"https://placeimg.com/140/140/any\",\n        }}\n      /\u003e\n      {Platform.OS === \"android\" \u0026\u0026 \u003cKeyboardAvoidingView behavior=\"padding\" /\u003e}\n    \u003c/View\u003e\n  );\n};\n```\n\nこれが GiftedChat のデフォルトの見た目となります。\n試しに、適当に文字を入力してみましょう。\n以下のような画面になります。\n\n| iOS                                                                           |\n| ----------------------------------------------------------------------------- |\n| \u003cimg src=\"./screenshots/chat-init.png\" alt=\"Initial Chat Screen\" width=\"320\"\u003e |\n\nチャット画面の見た目はこれだけで完了となります！\n\n`react-native-gifted-chat` はチャット画面を作成するのに必要な機能がたくさん備わっており、吹き出しの見た目の変更や`入力中...`と表示させたり、柔軟な対応ができます。\n\n今回のハンズオンでは、最低限の見た目しか作成しませんが、興味がある人はここから更に自分だけのチャットアプリの見た目を作っていくのも良いでしょう。\n\n詳しくは [react-native-gifted-chat のリポジトリ](https://github.com/FaridSafi/react-native-gifted-chat)を参照してみてください。\n\nチャットの見た目を Slack の用に変更するための設定も同作者が [example](https://github.com/FaridSafi/react-native-gifted-chat/tree/master/example-slack-message/src) を提供しています。\n\n### iii. ファイルの分割\n\n今回作成するアプリはチャット画面の他に、ログイン画面や新規登録画面など、複数の画面を作成することになります。\n\nこれらを全て `App.js` の中で書くことも可能ではありますが、コードの見通しがとても悪くなるので、画面ごとにファイルを分割します。\n\nまずは、各画面を入れる、`src/screens` ディレクトリを新しく作成しましょう。\n\n```\nmkdir src/screens\n```\n\nそして、先程 `App.js` に書いた内容を、`src/screens/ChatScreen.js` にコピーします。\n\nこの際、export する関数の名前を `App` から `ChatScreen` に変更します。\n\n```jsx\n// src/screens/ChatScreen.js\nimport * as React from \"react\";\nimport {\n  SafeAreaView,\n  View,\n  StyleSheet,\n  KeyboardAvoidingView,\n} from \"react-native\";\nimport { GiftedChat } from \"react-native-gifted-chat\";\n\nconst styles = StyleSheet.create({\n  container: { flex: 1 },\n});\n\nexport const ChatScreen = () =\u003e {\n  // 表示するチャットメッセージの配列\n  const [messages, setMessages] = React.useState([]);\n\n  React.useEffect(() =\u003e {\n    setMessages([\n      {\n        _id: 1,\n        text: \"Hello developer\",\n        createdAt: new Date(),\n        user: {\n          _id: 2,\n          name: \"React Native\",\n          avatar: \"https://placeimg.com/140/140/any\",\n        },\n      },\n    ]);\n  }, []);\n\n  const onSend = React.useCallback((messages = []) =\u003e {\n    setMessages((previousMessages) =\u003e\n      GiftedChat.append(previousMessages, messages)\n    );\n  }, []);\n\n  return (\n    \u003cSafeAreaView style={styles.container}\u003e\n      \u003cView style={styles.container}\u003e\n        \u003cGiftedChat\n          messages={messages}\n          placeholder=\"メッセージを入力してください...\"\n          onSend={onSend}\n          user={{\n            _id: 1,\n            name: \"Me\",\n            avatar: \"https://placeimg.com/140/140/any\",\n          }}\n        /\u003e\n        {Platform.OS === \"android\" \u0026\u0026 (\n          \u003cKeyboardAvoidingView behavior=\"padding\" /\u003e\n        )}\n      \u003c/View\u003e\n    \u003c/SafeAreaView\u003e\n  );\n};\n```\n\n`App.js` の中身を以下の用に変更しましょう。\n\n```jsx\n// App.js\nimport * as React from \"react\";\nimport { ChatScreen } from \"./src/screens/ChatScreen\";\n\nexport default function App() {\n  return \u003cChatScreen /\u003e;\n}\n```\n\nこれで、先程作った画面と同じ画面がシミュレータ上に表示されます。\n\nアプリの見た目上はなにも変わっていませんが、これでアプリに更に画面を追加する準備ができました。\n\n## 2. react-navigation のセットアップ\n\nこのステップでは、アプリに画面を追加するために、`react-navigation` というライブラリのセットアップをします。\n\n### i. 必要ライブラリのインストール\n\nreact-navigation の使用には、必要になるライブラリの数が少し多いので、コマンドをコピペして進めていきましょう。\n\n以下のコマンドを入力してください。\n\n```\nexpo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view\n```\n\nこれらのパッケージは react-native の画面遷移に使うライブラリである react-navigation に必要になるパッケージ郡となります。\n次に、画面遷移用のライブラリである `react-navigation` 類をインストールします。\n\n```\nexpo install @react-navigation/native @react-navigation/stack\n```\n\nこれで、react-navigation を使う準備はできました。\n\n### ii. NavigationContainer の設定\n\n`App.js` の中身を以下の用に変更しましょう。\n\n```jsx\n// App.js\nimport \"react-native-gesture-handler\";\nimport * as React from \"react\";\nimport { NavigationContainer } from \"@react-navigation/native\";\n\nexport default function App() {\n  return \u003cNavigationContainer\u003e\u003c/NavigationContainer\u003e;\n}\n```\n\n最初に `react-native-gesture-handler` を import しているのは、プロジェクト全体で画面遷移のためのスワイプ等のジェスチャーを有効にするために必要な設定です。\n\n```jsx\nimport \"react-native-gesture-handler\";\n```\n\n`NavigationContainer` 自体はなにも見た目を持ちませんが、現在表示している画面や、画面遷移のために必要な Context が含まれています。基本的に react-navigation を使用するアプリは全てこの `NavigationContainer` の中で行われます。\n\n```jsx\n\u003cNavigationContainer\u003e\u003c/NavigationContainer\u003e\n```\n\nこの段階ではアプリ上にはなにも表示されません。\n\n次に、NavigationContainer の中に追加する `Stack` というものを作成していきます。\n\n### iii. MainStack の作成\n\n`Stack` は react-navigation の基本的な画面郡のことを指します。\n\nStack の中には複数の画面を設けることができ、react-navigation の機能を使って、その画面を行き来することになります。\n\nStack 自体を複数作成することもでき、用途によって、Stack を分けたりすることもできます。\n\nこのハンズオンでは、ログイン前の画面郡を管理する `AuthStack` 及び、ログイン後の画面郡を管理する `MainStack` の 2 つの Stack を作成します。\n\nnavigation 関連のファイルは自分の場合、`navigation` というディレクトリを作成して、そこにまとめる場合が多いです。\n\n早速新しく `src/navigations` ディレクトリを作成しましょう。\n\n```\nmkdir src/navigations\n```\n\nそして、`src/navigations` に新しく `MainStack.js` を作成して、以下のように記入しましょう。\n\n```jsx\n// src/navigations/MainStack.js\nimport * as React from \"react\";\nimport { createStackNavigator } from \"@react-navigation/stack\";\n\nimport { ChatScreen } from \"../screens/ChatScreen\";\n\nconst Stack = createStackNavigator();\n\nexport const MainStack = () =\u003e {\n  return (\n    \u003cStack.Navigator initialRouteName=\"Chat\"\u003e\n      \u003cStack.Screen name=\"Chat\" component={ChatScreen} /\u003e\n    \u003c/Stack.Navigator\u003e\n  );\n};\n```\n\n`createStackNavigator` は `Stack.Navigator` と `Stack.Screen` の 2 つのコンポーネントを作成する関数です。\n\n`Stack.Navigator` は Stack の Context を保持するコンポーネントです。\n\n`initialRouteName` はその Stack 内で最初に表示する Screen 名を設定します。\n\n`Stack.Screen` は Stack の中で表示する画面を保持します。\n\n今回作成した `MainStack` はチャット画面を表示する `ChatScreen` のみを入れています。\n\n\b 次に、作成した `MainStack` を先程作成した `NavigationContainer` の中に入れましょう。\n\n```jsx\n// App.js\nimport \"react-native-gesture-handler\";\nimport * as React from \"react\";\nimport { NavigationContainer } from \"@react-navigation/native\";\nimport { MainStack } from \"./src/navigations/MainStack\";\n\nexport default function App() {\n  return (\n    \u003cNavigationContainer\u003e\n      \u003cMainStack /\u003e\n    \u003c/NavigationContainer\u003e\n  );\n}\n```\n\nこれで、先程作ったチャット画面がまた表示されるようになったはずです。\n\n次はアカウント新規登録用の画面やログイン用の画面を作成していきます。\n\n## 4. ログイン画面の作成\n\nこの章では、アカウントの新規登録、及びログインのための画面を作成します\n\n### i. AuthStack の作成\n\nまずは\n`src/navigations` に新しく `AuthStack.js` を作成しましょう。\n\n```jsx\n// src/navigations/AuthStack.js\nimport * as React from \"react\";\nimport { createStackNavigator } from \"@react-navigation/stack\";\n\nconst Stack = createStackNavigator();\n\nexport const AuthStack = () =\u003e {\n  return \u003cStack.Navigator headerMode=\"none\"\u003e\u003c/Stack.Navigator\u003e;\n};\n```\n\nこれは、ログインする前にユーザーが見れる画面郡を定義するファイルとなります。\n\nこの時点では、AuthStack の中になんの画面も定義されていない状態です。\n\nひとまず、この AuthStack も NavigationContainer の中に含めましょう\n\n`App.js` を以下のように変更します。\n\n```jsx\n// App.js\nimport \"react-native-gesture-handler\";\nimport * as React from \"react\";\nimport { NavigationContainer } from \"@react-navigation/native\";\nimport { AuthStack } from \"./src/navigations/AuthStack\";\nimport { MainStack } from \"./src/navigations/MainStack\";\nexport default function App() {\n  const loggedIn = false;\n  return (\n    \u003cNavigationContainer\u003e\n      {loggedIn ? \u003cMainStack /\u003e : \u003cAuthStack /\u003e}\n    \u003c/NavigationContainer\u003e\n  );\n}\n```\n\nこのように、ユーザーがログインしているかどうかを判断して、表示する画面を分けることができます。\n今はまだ、ログイン処理を書いていないため、非ログイン状態であることをハードコードしています。\n\n### ii. 必要なパッケージのインストール\n\nログイン画面、新規登録画面の見た目は `react-native-paper` という UI ライブラリを使って作成します。\n\n```\nyarn add react-native-paper\n```\n\n### iii. ログイン画面の見た目の作成\n\n次に、簡易的なログイン用の画面を作成しましょう。\n\n`src/screens` に新しく、`LoginScreen.js` を作成しましょう。\n\n```jsx\n// src/screens/LoginScreen.js\nimport * as React from \"react\";\nimport { View, StyleSheet, SafeAreaView } from \"react-native\";\nimport { Title, TextInput, Button } from \"react-native-paper\";\n\nconst styles = StyleSheet.create({\n  flex: {\n    flex: 1,\n  },\n  paddingLarge: {\n    padding: 24,\n  },\n  marginBottom: {\n    marginBottom: 16,\n  },\n  paddingSm: {\n    padding: 8,\n  },\n});\n\nexport const LoginScreen = () =\u003e {\n  const [email, setEmail] = React.useState(\"\");\n  const [password, setPassword] = React.useState(\"\");\n\n  return (\n    \u003cSafeAreaView style={styles.flex}\u003e\n      \u003cView style={[styles.flex, styles.paddingLarge]}\u003e\n        \u003cTitle style={styles.marginBottom}\u003eログイン\u003c/Title\u003e\n        \u003cTextInput\n          label=\"Email\"\n          value={email}\n          onChangeText={setEmail}\n          autoCapitalize=\"none\"\n          keyboardType=\"email-address\"\n          style={styles.marginBottom}\n        /\u003e\n        \u003cTextInput\n          label=\"Password\"\n          value={password}\n          onChangeText={setPassword}\n          secureTextEntry\n          style={styles.marginBottom}\n        /\u003e\n        \u003cButton\n          mode=\"contained\"\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={() =\u003e {}} // TODO: 仮の値を入れている\n        \u003e\n          ログイン\n        \u003c/Button\u003e\n        \u003cButton\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={() =\u003e {}} // TODO: 仮の値を入れている\n        \u003e\n          アカウントを作成する\n        \u003c/Button\u003e\n      \u003c/View\u003e\n    \u003c/SafeAreaView\u003e\n  );\n};\n```\n\n以下のような見た目になります。\n\n| iOS                                                                    | Android                                                                            |\n| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |\n| \u003cimg src=\"./screenshots/login.png\" alt=\"Login Screen iOS\" width=\"320\"\u003e | \u003cimg src=\"./screenshots/login-android.png\" alt=\"Login Screen Android\" width=\"320\"\u003e |\n\nこの画面を `AuthStack.js` 上で表示する用にします。\n\n先程作成した `AuthStack.js` を以下のように変更しましょう。\n\n```jsx\n// src/navigations/AuthStack.js\nimport * as React from \"react\";\nimport { createStackNavigator } from \"@react-navigation/stack\";\nimport { LoginScreen } from \"../screens/LoginScreen\"; // この部分を追加\n\nconst Stack = createStackNavigator();\n\nexport const AuthStack = () =\u003e {\n  return (\n    \u003cStack.Navigator initialRouteName=\"Login\" headerMode=\"none\"\u003e\n      \u003cStack.Screen name=\"Login\" component={LoginScreen} /** この部分を追加 */\u003e\n    \u003c/Stack.Navigator\u003e\n  );\n};\n```\n\nこれで、エミュレータ上で、先程作成したログイン画面が表示されるようになります。\n\n### iv. 新規登録画面の見た目の作成\n\n続いて、アカウント新規登録用の画面を作成しましょう。\n\n`src/screens` に新しく、`SignUpScreen.js` を作成しましょう。\n\n中身はほとんど、`LoginScreen.js`と同じです。\n\n```jsx\n// src/screens/SignUpScreen.js\nimport * as React from \"react\";\nimport { View, StyleSheet, SafeAreaView } from \"react-native\";\nimport { Title, TextInput, Button } from \"react-native-paper\";\n\nconst styles = StyleSheet.create({\n  flex: {\n    flex: 1,\n  },\n  paddingLarge: {\n    padding: 24,\n  },\n  marginBottom: {\n    marginBottom: 16,\n  },\n  paddingSm: {\n    padding: 8,\n  },\n});\n\nexport const SignUpScreen = () =\u003e {\n  const [email, setEmail] = React.useState(\"\");\n  const [password, setPassword] = React.useState(\"\");\n\n  return (\n    \u003cSafeAreaView style={styles.flex}\u003e\n      \u003cView style={[styles.flex, styles.paddingLarge]}\u003e\n        \u003cTitle style={styles.marginBottom}\u003e新規登録\u003c/Title\u003e\n        \u003cTextInput\n          label=\"Email\"\n          value={email}\n          onChangeText={setEmail}\n          autoCapitalize=\"none\"\n          keyboardType=\"email-address\"\n          style={styles.marginBottom}\n        /\u003e\n        \u003cTextInput\n          label=\"Password\"\n          value={password}\n          onChangeText={setPassword}\n          secureTextEntry\n          style={styles.marginBottom}\n        /\u003e\n        \u003cButton\n          mode=\"contained\"\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={() =\u003e {}} // TODO: 仮の値を入れている\n        \u003e\n          新規登録\n        \u003c/Button\u003e\n        \u003cButton\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={() =\u003e {}} // TODO: 仮の値を入れている\n        \u003e\n          既存アカウントでログインする\n        \u003c/Button\u003e\n      \u003c/View\u003e\n    \u003c/SafeAreaView\u003e\n  );\n};\n```\n\nこの画面も `LoginScreen` と同じく、 `AuthStack.js` 上で表示する用にします。\n\n`AuthStack.js` を以下のように変更しましょう。\n\n```jsx\n// src/navigations/AuthStack.js\nimport * as React from \"react\";\nimport { createStackNavigator } from \"@react-navigation/stack\";\nimport { LoginScreen } from \"../screens/LoginScreen\";\nimport { SignUpScreen } from \"../screens/SignUpScreen\"; // この部分を追加\n\nconst Stack = createStackNavigator();\n\nexport const AuthStack = () =\u003e {\n  return (\n    \u003cStack.Navigator initialRouteName=\"Login\" headerMode=\"none\"\u003e\n      \u003cStack.Screen name=\"Login\" component={LoginScreen} /\u003e\n      \u003cStack.Screen name=\"SignUp\" component={SignUpScreen} /** この部分を追加　*/\u003e\n    \u003c/Stack.Navigator\u003e\n  );\n};\n```\n\nこれで、ログイン画面と新規登録画面が AuthStack の中に含まれました。\n\nしかし、どうやってこれらの画面を行き来すればいいのでしょう？\n\n### v. 画面遷移を実装する\n\nここでは、ログイン画面と新規登録画面を行き来するためのロジックを実装します。\n\n画面の遷移には、`@react-navigation/native` が提供している `useNavigation` という関数を使用します。\n\nまずは、先程作成したログイン画面、`LoginScreen.js` に以下の内容を追記します。\n\n````jsx\n// src/screens/LoginScreen.js\nimport { useNavigation } from '@react-navigation/native'\n\n...\n\nexport const LoginScreen = () =\u003e {\n    const navigation = useNavigation()\n    ...\n\n    return (\n      ...\n      \u003cButton\n        style={[styles.paddingSm, styles.marginBottom]}\n        onPress={() =\u003e {\n          navigation.navigate(\"SignUp\");\n        }}\n      \u003e\n        アカウントを作成する\n      \u003c/Button\u003e\n      ...\n    );\n}\n```\n\n`useNavigation` は呼び出すと、その Context 内の情報を持つ `navigation` インスタンスを作成します。\n\n```js\nconst navigation = useNavigation();\n````\n\n`navigation` は画面を行き来するための`navigate`等のメソッドを持っています。\n\n```js\nnavigation.navigate(\"SignUp\");\n```\n\n`navigate` の引数に入れる文字列は、`Stack.Screen` に定義した `name` の値を入れます。\n\nこれで、ログイン画面の`アカウントを作成する`をタップしたら、新規登録画面に行けるようになりました。\n\nこのままでは、ログイン画面に戻れないので、新規登録画面にも同じ用に以下の内容を追記します。\n\n```jsx\n// src/screens/SignUpScreen.js\nimport { useNavigation } from '@react-navigation/native'\n\n...\n\nexport const SignUpScreen = () =\u003e {\n    const navigation = useNavigation()\n    ...\n\n    return (\n      ...\n      \u003cButton\n        style={[styles.paddingSm, styles.marginBottom]}\n        onPress={() =\u003e {\n          navigation.navigate(\"Login\");\n        }}\n      \u003e\n        既存アカウントにログインする\n      \u003c/Button\u003e\n      ...\n    );\n}\n```\n\nこれでログイン画面の新規登録画面を行き来できる用になりました。\n\n今回のハンズオンで作成する見た目は以上となります。\n\n次の章から、firebase を使って実際のアカウントの作成や、チャットのログを db に保管する用に変更していきます。\n\n## 5. firebase との連携:アカウント登録\n\nこの章では、firebase を使ったアカウントの作成や、db との接続を行います。\n\n### i. firebase プロジェクトの作成。\n\nまずは、新しく firebase のプロジェクトをこのハンズオン用に作成します。\n\n[Firebase のコンソール](https://console.firebase.google.com/)にアクセスして、お使いの Google アカウントでログインしてください。\n\nFirebase のプロジェクトを作るのが初めての場合、以下の様な画面が表示されます。\n\n`Create a Project` をクリックしてください。\n\n![Firebase Home](./screenshots/firebase-init.png)\n\n好きなプロジェクト名を入力して、プロジェクトを作成します。\n\n作成した後の画面にて、プラットフォームを選択する画面があるので、`Web` を選びましょう。\n\n![Firebase Web](./screenshots/firebase-web.png)\n\n好きなアプリ名を入力して、アプリの設定を作成します。\n\n\b`firebaseConfig` に firebase に必要な設定値が表示されているので、それをコピーしましょう。この値は `iii` で使います。\n\n![Firebase Config](./screenshots/firebase-config.png)\n\n### ii. authentication の設定\n\nこのハンズオンでは、email と password を使ってアカウントの新規登録やログイン処理を行います。\n\nfirebase console の画面左に表示されるメニューから Authentication を選択し、email/password でのログインができるように設定しましょう。\n\n### iii. firebase sdk のインストール\n\n今回のハンズオンのプロジェクト内でも firebase を呼び出せるようにするために、firebase をインストールしましょう\n\n```\nyarn add firebase\n```\n\n### iv. firebase の設定\n\nアプリのルートに　 \b`firebase.js` を作成して、以下のように記載しましょう。\n\n`firebaseConfig` には、先程コピーした firebase の設定値を入れましょう。\n\n```js\n// firebase.js\n\nimport * as firebase from \"firebase\";\nimport \"firebase/auth\";\nimport \"firebase/firestore\";\n\n/**\n * コピーしたFirebase の Config を入れる\n */\nconst firebaseConfig = {\n  apiKey: \"\",\n  authDomain: \"\",\n  projectId: \"\",\n  storageBucket: \"\",\n  messagingSenderId: \"\",\n  appId: \"\",\n  measurementId: \"\",\n};\n\nlet app;\nif (firebase.apps.length === 0) {\n  app = firebase.initializeApp(firebaseConfig);\n} else {\n  app = firebase.app();\n}\n\nconst db = app.firestore();\nconst auth = firebase.auth();\n\nexport { db, auth };\n```\n\nこれで、認証情報を扱う `auth` と DB にアクセスする `db` が使えるようになります。\n\n### v. 新規登録処理の実装\n\n早速、`SignUpScreen.js` に新規登録のロジックを追加しましょう。\n\n```jsx\n// src/screens/SignUpScreen.js\n...\nimport { auth } from \"../../firebase\";\n\n...\n\nexport const SignUpScreen = () =\u003e {\n\n    ...\n\n  /**\n   * アカウントを登録する\n   */\n  const signup = () =\u003e {\n    auth\n      .createUserWithEmailAndPassword(email, password)\n      .then(() =\u003e {\n        console.log(\"Register Success!\");\n      })\n      .catch((error) =\u003e {\n        console.error(error.message);\n      });\n  };\n\n    return (\n      ...\n        \u003cButton\n          mode=\"contained\"\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={signup}\n        \u003e\n          新規登録\n        \u003c/Button\u003e\n      ...\n    );\n}\n```\n\n`createUserWithEmailAndPassword` は、引数に渡した email と password でアカウントを作成するためのメソッドです。\n\n\u003e 参考：[firebase のドキュメント: createUserWithEmailAndPassword](https://firebase.google.com/docs/auth/web/start)\n\nこれで入力した email とパスワードによってアカウントの新規登録が可能となります。\n\n試しに、シミュレータで新規登録画面を開き、ご自身の email を入力して、パスワードは適当に 5 文字以下の文字列を入力して「新規登録」ボタンを押してみましょう。\n\nfirebase のアカウントはデフォルトで 5 文字以下のパスワードは使えないようになっているので、エラーが表示されます。\n\nあたらめて、6 文字以上のパスワードでアカウントを新規登録してみましょう。\n\nconsole の方を見ると、`Register Success!` というログが表示されるでしょう。\n\nしかし、画面の方はなにも変化がありません。\n\nこれは、ログインしたときに、自分がログイン状態であるかどうかを識別していないからとなります。\n\n### vi. AuthState に subscribe する\n\n自分がログイン状態であるかを判別するのは、`MainStack` と `AuthStack` を切り替える `App.js` の中で行います。\n\n`App.js` の中身を以下のように変更してみましょう。\n\n```jsx\n// App.js\nimport \"react-native-gesture-handler\";\nimport * as React from \"react\";\nimport { NavigationContainer } from \"@react-navigation/native\";\nimport { AuthStack } from \"./src/navigations/AuthStack\";\nimport { MainStack } from \"./src/navigations/MainStack\";\nimport { auth } from \"./firebase\";\n\nexport default function App() {\n  const [loggedIn, setLoggedIn] = React.useState(false);\n\n  React.useEffect(() =\u003e {\n    const unsubscribe = auth.onAuthStateChanged((user) =\u003e {\n      if (user) {\n        // ログイン中\n        setLoggedIn(true);\n      } else {\n        // ログアウト中\n        setLoggedIn(false);\n      }\n    });\n    return unsubscribe;\n  });\n\n  return (\n    \u003cNavigationContainer\u003e\n      {loggedIn ? \u003cMainStack /\u003e : \u003cAuthStack /\u003e}\n    \u003c/NavigationContainer\u003e\n  );\n}\n```\n\n`auth.onAuthStateChanged` はログイン状態が変更されたかどうかのイベントを待つ Listener となります。\nログイン状態が変更された時に、ここに入れた callback が発火されます。\n\n\u003e 参考：[firebase のドキュメント: onAuthStateChanged](https://firebase.google.com/docs/auth/web/manage-users)\n\nこれで、user 情報がある際は `loggedIn` 状態を true にすることで、チャット画面を持つ `MainStack` に画面が切り替わる用になります。\n\n再び、新規登録画面にてアカウントを登録してみましょう。\n\n先程作成したアカウントはすでに登録済みのアカウントのため、エラーが出てしまいます。\n\nなので、メールアドレスに `+1` をつける等で別の email 扱いにしてアカウントを作成すると、今度はチャット画面に以降できる用になります。\n\n### vii. ログアウト処理の実装\n\n今度はログイン機能を試してみたいところですが、一度ログイン状態になったらセッションが切れるまでログイン状態になってしまいます。\nなので、先にログアウトするための処理を実装します。\n\n`ChatScreen.js` に以下のように追記しましょう。\n\n```jsx\n// src/screens/ChatScreen.js\n...\nimport { useNavigation } from \"@react-navigation/core\";\nimport { Button } from \"react-native-paper\";\nimport { auth } from \"../../firebase\";\n\n...\n\nexport const ChatScreen = () =\u003e {\n  const navigation = useNavigation();\n  const signOut = () =\u003e {\n    auth\n      .signOut()\n      .then(() =\u003e {\n        console.log(\"Sign out success!\");\n      })\n      .catch((error) =\u003e {\n        console.error(error.message);\n      });\n  };\n\n  React.useLayoutEffect(() =\u003e {\n    navigation.setOptions({\n      headerRight: () =\u003e \u003cButton onPress={signOut}\u003eログアウト\u003c/Button\u003e,\n    });\n  }, []);\n  ...\n};\n\n```\n\nここでも `useNavigation` を使っています。\n\n今回は、画面上に表示されているヘッダーの右上に「ログアウト」ボタンを追加するために、`navigation.setOptions` を呼び出しています。\nヘッダーは `react-navigation` の管轄のため、この様な処理になっています。\n\n\u003e 参考: [react-navigation のドキュメント: Header interaction with its screen component](https://reactnavigation.org/docs/header-buttons/#header-interaction-with-its-screen-component)\n\n\u003e 参考: [firebase のドキュメント: signout](https://firebase.google.com/docs/reference/js/firebase.auth.Auth#signout)\n\nこれで、ログアウトボタンを押したら、ログイン画面に戻れるはずです。\n\n### viii. ログイン処理の実装\n\n新規登録と同じ用に、今度はログインの処理を実装してみましょう。\n\n`LoginScreen.js` に以下のように追記しましょう。\n\n```jsx\n// src/screens/LoginScreen.js\n...\nimport { auth } from \"../../firebase\";\n\n...\n\nexport const LoginScreen = () =\u003e {\n\n    ...\n\n  /**\n   * ログインする\n   */\n  const signin = () =\u003e {\n    auth\n      .signInWithEmailAndPassword(email, password)\n      .then(() =\u003e {\n        console.log(\"Signin Success!\");\n      })\n      .catch((error) =\u003e {\n        console.error(error.message);\n      });\n  };\n\n    return (\n      ...\n        \u003cButton\n          mode=\"contained\"\n          style={[styles.paddingSm, styles.marginBottom]}\n          onPress={signin}\n        \u003e\n          ログイン\n        \u003c/Button\u003e\n      ...\n    );\n}\n```\n\n`signInWithEmailAndPassword` は、引数に渡した email と password でアカウントを作成するためのメソッドです。\n\n\u003e 参考：[firebase のドキュメント: signinwithemailandpassword](https://firebase.google.com/docs/reference/js/firebase.auth.Auth#signinwithemailandpassword)\n\nこれでログイン処理も完了となります。\n\n## 6. firebase との連携:db 接続\n\nこの章がこのハンズオンの最終章です。\n\nこの章では、firebase 上にチャットの履歴を保存して、それをアプリ上で表示するようにしていきます。\n\n### i. fire store のセットアップ\n\nまずはチャットの履歴を保存するための DB をセットアップします。\n\nfirebase のコンソールを開き、左のメニューから `Firestore Database` を選択して、`Create new database` を選択します。\n\n`production` と `test` の選択肢がありますが、今回のハンズオンでは `test` を使います。\n\n画面を進めていけば test 用の db が作成されます。\n\nこれで、チャットの履歴を保存する準備は完了です。\n\n### ii. DB を使う用にチャット画面の実装を変更\n\n`ChatScreen.js` を以下のように変更します。\n\n```jsx\n// src/screens/ChatScreen.js\nimport * as React from \"react\";\nimport {\n  SafeAreaView,\n  View,\n  StyleSheet,\n  KeyboardAvoidingView,\n} from \"react-native\";\nimport { GiftedChat } from \"react-native-gifted-chat\";\nimport { useNavigation } from \"@react-navigation/core\";\nimport { Button } from \"react-native-paper\";\nimport { auth, db } from \"../../firebase\"; // この部分に db を追加\nconst styles = StyleSheet.create({\n  container: { flex: 1 },\n});\n\nexport const ChatScreen = () =\u003e {\n  const navigation = useNavigation();\n  const signOut = () =\u003e {\n    auth\n      .signOut()\n      .then(() =\u003e {\n        console.log(\"Sign out success!\");\n      })\n      .catch((error) =\u003e {\n        console.error(error.message);\n      });\n  };\n\n  React.useLayoutEffect(() =\u003e {\n    navigation.setOptions({\n      headerRight: () =\u003e \u003cButton onPress={signOut}\u003eログアウト\u003c/Button\u003e,\n    });\n  }, []);\n\n  // 表示するチャットメッセージの配列\n  const [messages, setMessages] = React.useState([]);\n\n  // 以前 useEffect だった部分を消し、以下のコードを追加\n  React.useLayoutEffect(() =\u003e {\n    const unsubscribe = db\n      .collection(\"chats\")\n      .orderBy(\"createdAt\", \"desc\")\n      .onSnapshot((snapshot) =\u003e\n        setMessages(\n          snapshot.docs.map((doc) =\u003e ({\n            _id: doc.data()._id,\n            createdAt: doc.data().createdAt.toDate(),\n            text: doc.data().text,\n            user: doc.data().user,\n          }))\n        )\n      );\n    return unsubscribe;\n  }, []);\n\n  const onSend = React.useCallback((messages = []) =\u003e {\n    setMessages((previousMessages) =\u003e\n      GiftedChat.append(previousMessages, messages)\n    );\n    // 以下の部分を追加\n    const { _id, createdAt, text, user } = messages[0];\n    db.collection(\"chats\").add({\n      _id,\n      createdAt,\n      text,\n      user,\n    });\n  }, []);\n\n  return (\n    \u003cSafeAreaView style={styles.container}\u003e\n      \u003cView style={styles.container}\u003e\n        \u003cGiftedChat\n          messages={messages}\n          placeholder=\"メッセージを入力してください...\"\n          onSend={onSend}\n          user={{\n            _id: auth?.currentUser?.email, // ここを変更\n          }}\n        /\u003e\n        {Platform.OS === \"android\" \u0026\u0026 (\n          \u003cKeyboardAvoidingView behavior=\"padding\" /\u003e\n        )}\n      \u003c/View\u003e\n    \u003c/SafeAreaView\u003e\n  );\n};\n```\n\nこれにて、簡易チャットアプリの完成です！\n\n可能であれば、2 つのシミュレータを立ち上げてみて、チャット機能が動くかどうかを試してみましょう！\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnaturalclar%2Fexpo-firebase-chat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnaturalclar%2Fexpo-firebase-chat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnaturalclar%2Fexpo-firebase-chat/lists"}