20211012のReactに関する記事は5件です。

【React Native】FCMの設定でつまずいたこと(Androidのみ)

1、FCM(Firebase Cloud Messaging)とは Firebaseが提供しているアプリにプッシュ通知を送る機能です。 機能自体無料で使えて、アプリ側ではfirebaseから取得したfcmトークンをfcmに設定するだけでプッシュ通知を受け取れるためかなり便利です。 本格的なアプリを作るとなるとプッシュ通知は必要不可欠です。 https://firebase.google.com/docs/cloud-messaging?hl=ja 2、FCMの簡単な仕組み・設定方法 https://firebase.google.com/?hl=ja ↑Firebaseでプロジェクトを作成し、左のメニュー欄からCloud Messagingを選択します 開始方法からiOS、Android両方の設定をします。 iOSの方はAPNs認証キーの設定も必要なため、Apple Developerの登録も必要です。 今回はAndroidのみですのでそこは割愛します。   https://rnfirebase.io/messaging/usage 次にアプリ側でFCMトークンを取得し、そのFCMごとに識別して通知が送られてきます。 本題 プロジェクトでFCMを使うことになり、アプリの管理画面から通知文を作成し、APIからFCMにリクエストを出してFCMを経由してアプリに通知が届くような仕様でした。 今回FCMの実装をしている時ににつまずいたことは ・FCMの画面から直接トークンを指定して、送った場合→通知が届き、タップするとアプリが起動する ・アプリの管理画面から通知を送信し、APIからリクエストを出してFCMを経由して送った場合→通知は届くがタップしてもアプリが起動しない という件でした。 なぜか管理画面から送る通知はタップしても起動することなく、そのまま通知メッセージが消えるだけでした。 原因 FCMから直接送った通知のデータと管理画面でAPIを使って送った通知のデータを見比べてみたところ原因が判明しました。 FCMから返ってくるデータを取得するにはsetBackgroundMessageHandlerを使います。 アプリがバックグラウンドにある状態もしくはキルしている状態の時、プッシュ通知が送られてきた時にFCMからのレスポンスを取得することができます。 以下のコードをindex.jsに追記するとremoteMessageにデータが入っています。 import messaging from '@react-native-firebase/messaging'; messaging().setBackgroundMessageHandler(async remoteMessage => { console.log('Message handled in the background!', remoteMessage); }); ちなみにフォアグラウンド状態でも通知のデータを受け取れますが今回は割愛します。 データを比べてみる 1、firebaseでタイトルとテキストを入力、fcm通知を選んで送信 {  "notification": {  "android": {},  "body": "通知本文",  "title": "通知タイトル"  },  "from": number,  "ttl":number,  "sentTime": 1633065375430,  "data": {}, "messageId": "${messageId}", "collapseKey": "バンドルID" } 2、管理画面(API)から通知を送信 {  "notification": {  "android": {   "clickAction": "attention"  },  "body": "通知本文" },  "sentTime": 1633065600374,  "data": {},  "from": "501097458848",  "messageId": "${messageId}",  "ttl": 2419200,  "collapseKey": "バンドルID" } 管理画面から送った方にはandroidオブジェクト内にclickActionが入っています。 これが原因でした。 https://firebase.google.com/docs/cloud-messaging/http-server-ref?hl=ja ここのclick_actionのところに記載されているとおり、 ユーザーが通知をクリックしたときに実行されるアクション。 これが指定されている場合、ユーザーが通知をクリックすると、一致するインテント フィルタを持つアクティビティが起動します。 clickActionを指定するとfcmトークンに紐ずくアプリを起動するのではなく、一致するインテントフィルタ(アプリで別途設定する値)に紐ずくアプリを起動させる挙動になります。 今回このインテントフィルタを設定していなかったので一致するインテントフィルタがなく、アプリが起動しませんでした。 ちなみにこのclickActionの使い道ですが、 例えば 1、LINEアプリからプロフィール欄のBGMを設定しませんか?と通知がくる 2、ユーザーがタップするとLINEアプリではなく、LINE MUSICアプリが起動し、すぐ設定が行える こんな挙動をしたい時は便利かもしれません。 ただ別のアプリを起動させる必要もないため、今回はclickActionは削除する方針にしました。 clickActionを削除すると問題なくアプリが起動しました! 補足 アプリ起動後、通知があるページ(お知らせ画面)に遷移させたいときは 1、アプリがバックグラウンド状態の時:onNotificationOpenedApp 2、アプリを閉じている状態の時:getInitialNotification この二つを初期マウント時(useEffect)に走らせればデータを取得できます。 そのデータから通知のidを受け取りそれに紐ずく画面に遷移すれば意図した挙動になるかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useStateとuseEffectの違いについて

よくわからなかったので自分の理解のためにまとめました。 useState() | state useState() を利用すると、コンポーネントの状態管理を実装できる。 カウント機能の実装を例にclassを使う場合と比較してみていくこととする。 class.jsx class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } 関数コンポーネントには this は存在しないので、this.state を読み書きすることはできないため、代わりに、コンポーネントの内部で直接 useState フックを使う。 usestate.jsx import React, { useState } from 'react'; function Example() { // 新しいステート変数を宣言し、これを "count "と呼ぶ const [count, setCount] = useState(0); usestateを叩いた時に起きること 『state 変数』が宣言される。 useState は、クラス内での this.state と同じ機能を実現することができる。 つまり、通常、関数が終了すると変数は『消えて』しまうが、state 変数は React によって保持される。 useState の引数 useState() の唯一の引数は state の初期値である。 useState の引数 現在の state と、それを更新するための関数である。 this.state.count と this.setState に似ているが、それをペアとして手に入れる点が異なる。 state の読み出し クラス内で現在のカウント値を表示したい場合にはthis.state.countを追記する必要がある。 class.jsx <p>You clicked {this.state.count} times</p> しかし、 usestate の場合には、直接 count を使うことができる. usestate.jsx <p>You clicked {count} times</p> state の更新 クラスでは、this.setState() を呼ぶことで count ステートを更新する class.jsx <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> すでに setCount と count を変数として受け取っているので、this は必要ない usestate.jsx <button onClick={() => setCount(count + 1)}> Click me </button> useEffect() | effect useEffectを使うと、useEffectに渡された関数は、レンダーの結果が画面に反映された後に動作する。 従来のReactで考えると、componentDidMount componentDidUpdateと同じように使える。 つまりuseEffectとは、「関数の実行タイミングをReactのレンダリング後まで遅らせるhook」である。 副作用(side-effect)の処理(DOMの書き換え、変数代入、API通信などUI構築以外の処理)を関数コンポーネントで扱える。 第1引数...副作用関数(戻り値はクリーンアップ関数、または何も返さない) 第2引数...副作用関数の実行タイミングを制御する依存データが入る(省略可) renderメソッドで副作用を起こすのは早すぎる場合にuseEffectを使う。 つまり React が DOM を更新したあとに起こしたいものを副作用として用いる。 useEffect は何をやっているのか? レンダー後に何かの処理をしないといけない、ということを React に伝えることができる。 React この渡たされた関数を覚えていて(これを「副作用(関数)」と呼ぶ)、DOM の更新の後にそれを呼び出す。 useEffect がコンポーネント内で呼ばれるのはなぜか? useEffect を記述することで、副作用内から state である count(や任意の props)にアクセスできるようになる。 useEffect はいつ呼ばれるのか? デフォルトでは、副作用関数は初回のレンダー時および毎回の更新時(毎回のレンダー後)に呼び出される。 特定の場合に副作用の適用をスキップするためには、useEffect のオプションの第 2 引数として配列を渡す。 <参考資料> ステートフックの利用法 副作用フックの利用法 フック早わかり フック API リファレンス React hooksを基礎から理解する (useEffect編)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのFirebase(デプロイまで)

はじめに 自分で実装するのにかなり苦戦したため、うまくいったときの手順を残しておきます。(2021年10月時点) 環境 Windows 10 Firebase(ver : 9.19.0) npm(ver : 6.14.13) Firebase側の設定 まずはFirebaseへアクセスし登録まで完了させます。 1.1. 新しいプロジェクトの作成 手順1/3  「プロジェクトを追加」ボタンを押下、適当にプロジェクト名を決めます。 手順2/3  このプロジェクトでGoogle アナリティクスを有効にするにチェック 手順3/3  Default Account for Firebaseを選択 1.2. プロジェクトの設定 リソースロケーションの設定 「プロジェクトの概要」横のプロジェクトの設定を押下。 プロジェクト欄にある「デフォルトの GCP リソース ロケーション」から地域を選択(日本の場合はasia-northeast1でいいと思います)。こちらは一度選択すると変更できないので注意。 マイアプリの設定 プロジェクト欄下にあるマイアプリから目的に応じたプラットフォームを選択 (例)ウェブアプリの場合 「</>」のアイコンを選択し、アプリの登録をします。 アプリのニックネームを適当に入力し、Firebase Hostingの設定にチェック(Web上に公開する際はチェック)したら、アプリを登録 ↓ Firebase SDKの追加 色々書いてありますが、npmを使用するを選択して次へ。 ↓ Firebase CLIのインストール 後で設定するのでこのまま次へ。 ↓ FireHostingへのデプロイ 後で設定するので、コンソールへ進むを選択 1.3. プランの変更 初めは無料のSparkプランですが、この状態ではデプロイができないため有料プラン(Blazeプラン)へアップグレードします。有料と言っても個人で開発する分には十分な容量が設定されているため、よほどのことがなければ請求が発生することはないと思います。 料金体制についてはこちらから。 これでいったんFirebase側の設定は完了です。 エディタでの設定 2.1. アプリディレクトリの作成 使用しているエディタでターミナルを開いて、Reactアプリの立ち上げを行います。ディレクトリ名はsampleとします。 C:\Users\Usename\> npx create-react-app sample しばらくすると作成完了するので、試しに見てみます。sampleディレクトリへ移動して、 ~/sample> npm start こんな感じで表示されれば成功です。 2.2. Firebaseのインストール ~ 設定 それではFirebaseの設定をしていきます。先ほど開いたターミナルで以下を実行します。 (グローバルインストールなのでPJごとに実行する必要はなく、使ってるPCで一度実行していればスキップ。) ~/sample> npm install -g firebase-tools ↓ 次にパッケージをインストールします。 ~/sample> npm install --save firebase ↓ コマンドプロンプトに移動し、Firebaseにログイン ~/sample> firebase login ↓ ログインできたら、Firebaseを初期化していきます。 ~/sample> firebase init ↓ initコマンドを実行すると以下の画面になり、初期化してよいか聞かれるのでYesを選択します。 ~/sample> firebase init ######## #### ######## ######## ######## ### ###### ######## ## ## ## ## ## ## ## ## ## ## ## ###### ## ######## ###### ######## ######### ###### ###### ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ######## ######## ## ## ###### ######## You're about to initialize a Firebase project in this directory: C:\Users\Username\projectname Before we get started, keep in mind: * You are initializing within an existing Firebase project directory ? Are you ready to proceed? (Y/n) ↓ どの機能を利用するか聞かれるので、スペースキーでセレクトします。ここではFirestore, Functions, Hostingを選んでみます。 ? Which Firebase features do you want to set up for this directory? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection) >( ) Realtime Database: Configure a security rules file for Realtime Database and (optionally) provision default instance ( ) Firestore: Configure security rules and indexes files for Firestore ( ) Functions: Configure a Cloud Functions directory and its files ( ) Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploys ( ) Hosting: Set up GitHub Action deploys ( ) Storage: Configure a security rules file for Cloud Storage ( ) Emulators: Set up local emulators for Firebase products (Move up and down to reveal more choices) ↓ Firebase側で先ほど作成したプロジェクトを選択していきます。 既存のプロジェクトを使うので「Use an existing project」を選択します。 === Project Setup First, let's associate this project directory with a Firebase project. You can create multiple project aliases by running firebase use --add, but for now we'll just set up a default project. ? Please select an option: (Use arrow keys) > Use an existing project Create a new project Add Firebase to an existing Google Cloud Platform project Don't set up a default project ↓ Firebase側で作成したプロジェクト名を選択。 ? Select a default Firebase project for this directory: (Use arrow keys) > sample-1e42f (sample ) ↓ 選んだ機能に応じてセットアップ方法を聞かれるため、エンターキーで進めていきます。基本的にはデフォルトの選択通りに進めていきます。 === Firestore Setup Firestore Security Rules allow you to define how and when to allow requests. You can keep these rules in your project directory and publish them with firebase deploy. ? What file should be used for Firestore Rules? (firestore.rules) Firestore indexes allow you to perform complex queries while maintaining performance that scales with the size of the result set. You can keep index definitions in your project directory and publish them with firebase deploy. ? What file should be used for Firestore indexes? (firestore.indexes.json) ↓ 次にCloud Function を記述する言語の選択をします。 === Functions Setup A functions directory will be created in your project with sample code pre-configured. Functions can be deployed with firebase deploy. ? What language would you like to use to write Cloud Functions? (Use arrow keys) > JavaScript TypeScript 本番環境用としてデプロイしたいので、デフォルトはpublicだけど、buildに変更。 === Hosting Setup Your public directory is the folder (relative to your project directory) that will contain Hosting assets to be uploaded with firebase deploy. If you have a build process for your assets, use your build's output directory. ? What do you want to use as your public directory? (public) ? What do you want to use as your public directory? build ↓ ここだけデフォルトNoだけど、Yesに変更。 ? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) ↓ GitHubとの連携を聞かれますが、デフォルト通りNoを選択。 ? Set up automatic builds and deploys with GitHub? (y/N) これで初期化完了! i Writing configuration info to firebase.json... i Writing project information to .firebaserc... + Firebase initialization complete! ルールの変更 initした際、sampleディレクトリの直下にfirestore.rulesのファイルが作成されているはずなので、ファイルを開きます。 firestore.rules rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read; write: if false; } } } デフォルトでは誰でも読み書き可能で脆弱な状態なので、下のように書き換えます。 firestore.rules rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read; allow write: if request.auth.uid != null; } } } これで誰でも見ることはできるけど、書き込みは認証されたユーザーしかできないようになりました。 関数の作成 ここまで終えたらもう少しです。 新たに作成されたfunctionsディレクトリ下に以下の関数がデフォルトで作成されます。 functions/index.js const functions = require('firebase-functions'); // Create and Deploy Your First Cloud Functions // https://firebase.google.com/docs/functions/write-firebase-functions exports.helloWorld = functions.https.onRequest((request, response) => { response.send("Hello from Firebase!"); }); 目的のアプリに応じて適宜関数を書き換えてください。とりあえずこのままで進めます。 ビルド これまで作成してきたコード内容でビルドします。ターミナルで以下のコマンドを実行します。 ~/sample> npm run build デプロイ いよいよデプロイです。以下のコマンドでデプロイを実行しましょう。 ~/sample> firebase deploy === Deploying to 'sample-1e42f'... i deploying firestore, functions, hosting i firestore: reading indexes from firestore.indexes.json... i cloud.firestore: checking firestore.rules for compilation errors... + cloud.firestore: rules file firestore.rules compiled successfully i functions: ensuring required API cloudfunctions.googleapis.com is enabled... i functions: ensuring required API cloudbuild.googleapis.com is enabled... ! functions: missing required API cloudbuild.googleapis.com. Enabling now... + functions: required API cloudfunctions.googleapis.com is enabled + functions: required API cloudbuild.googleapis.com is enabled i functions: preparing functions directory for uploading... i functions: packaged functions (24.51 KB) for uploading + firestore: deployed indexes in firestore.indexes.json successfully i firestore: latest version of firestore.rules already up to date, skipping upload... + functions: functions folder uploaded successfully i hosting[sample-1e42f]: beginning deploy... i hosting[sample-1e42f]: found 19 files in build + hosting[sample-1e42f]: file upload complete + firestore: released rules firestore.rules to cloud.firestore i functions: creating Node.js 14 function helloWorld(us-central1)... + functions[helloWorld(us-central1)]: Successful create operation. i functions: cleaning up build files... Function URL (helloWorld(us-central1)): https://us-central1-sample-1e42f.cloudfunctions.net/helloWorld i hosting[sample-1e42f]: finalizing version... + hosting[sample-1e42f]: version finalized i hosting[sample-1e42f]: releasing new version... + hosting[sample-1e42f]: release complete + Deploy complete! Project Console: https://console.firebase.google.com/project/********* Hosting URL: https://************** Deploy complete! が表示されれば成功です。おめでとうございます。 Hosting URLからページに飛ぶことができます。 ローカル環境と同じ画面になれば成功です。 デプロイ時のエラー こんなエラーが出ることがあります。 Running command: npm --prefix "$RESOURCE_DIR" run lint npm ERR! missing script: lint : : Error: spawn npm --prefix "%RESOURCE_DIR%" run lint ENOENT at notFoundError : : code: 'ENOENT', errno: 'ENOENT', syscall: 'spawn npm --prefix "%RESOURCE_DIR%" run lint', path: 'npm --prefix "%RESOURCE_DIR%" run lint', spawnargs: [] } Error: functions predeploy error: Command terminated with non-zero exit code1 どうやらlintが悪さしているようなので、firebase.json にある「npm --prefix "%RESOURCE_DIR%" run lint」を削除することでエラー解消します。 まとめ Firebase側の設定 npm install -g firebase-tools npm install --save firebase firebase login firebase init npm run build firebase deploy 途中エラー出ることもありますが、調べたら必ず出てくるのでめげずに頑張りましょう~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactを始めたい人へ。今日だけでできるTODO #21 useContext①

useContextとは? propsを直接渡したい子孫コンポーネントに値を渡すことができるフックです。 孫コンポーネントに値を受け渡す場合、親コンポーネントから子コンポーネントに受け渡した値をさらに孫コンポーネントに渡す。 ということが必要でしたがuseContextを利用することで直接を渡すことができます。 オブジェクト / コンポーネント 説明 Contextオブジェクト React.createContext()の戻り値 Providerコンポーネント Contextオブジェクトが保持しているコンポーネント Consumerコンポーネント useContext()を利用することで、Contextオブジェクトから値を取得できる Contextオブジェクト const ContextObj = React.createContext(); または、 import React, { createContext } from 'react'; const ContextObj = createContext(); Providerコンポーネント .Providerでラップするとラップされたコンポーネントのツリー内でvalueの値を参照できるようになります。 <ContextObj.Provider value={ツリー内で共有可能なコンテキストオブジェクトの値} > <Consumer /> </ContextObj.Provider> import時にuseContextを読み込みます。 import React, { useContext } from 'react'; const コンテキストオブジェクトの値 = useContext(コンテキストオブジェクト); useContextの利用 import React, { createContext, useContext } from 'react'; const ContextObj = createContext(); const Consumer = () => { const message = useContext(ContextObj); console.log(message); return <p>{message}</p>; }; const message = 'React useContext'; export default function App() { return ( <> <ContextObj.Provider value={message}> <Consumer /> </ContextObj.Provider> </> ); }; useContextでContextオブジェクトから値を取得し、変数messageに格納します。 const message = useContext(ContextObj); messageにはProviderコンポーネントにてvalueに指定された値を受け取ります。 const message = 'React useContext'; export default function App() { return ( <> <ContextObj.Provider value={message}> <Consumer /> </ContextObj.Provider> </> ); }; すると画面に「React useContext」が表示されると思います。 次はファイルを分割した場合を試してみたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

これからReact始めたい人のための今日だけでできるTODO#21 useContext①

useContextとは? propsを直接渡したい子孫コンポーネントに値を渡すことができるフックです。 孫コンポーネントに値を受け渡す場合親コンポーネントから子コンポーネントに受け渡した値をさらに孫コンポーネントに渡す。 ということが必要でしたがuseContextを利用することで直接を渡すことができます。 オブジェクト / コンポーネント 説明 Contextオブジェクト React.createContext()の戻り値 Providerコンポーネント Contextオブジェクトが保持しているコンポーネント Consumerコンポーネント useContext()を利用することで、Contextオブジェクトから値を取得できる Contextオブジェクト const ContextObj = React.createContext(); または、 import React, { createContext } from 'react'; const ContextObj = createContext(); Providerコンポーネント .Providerでラップするとラップされたコンポーネントのツリー内でvalueの値を参照できるようになります <ContextObj.Provider value={ツリー内で共有可能なコンテキストオブジェクトの値} > <Consumer /> </ContextObj.Provider> Consumerコンポーネント import時にuseContextを読み込みます。 import React, { useContext } from 'react'; const コンテキストオブジェクトの値 = useContext(コンテキストオブジェクト); useContextの利用 import React, { createContext, useContext } from 'react'; const ContextObj = createContext(); const Consumer = () => { const message = useContext(ContextObj); console.log(message); return <p>{message}</p>; }; const message = 'React useContext'; export default function App() { return ( <> <ContextObj.Provider value={message}> <Consumer /> </ContextObj.Provider> </> ); }; useContextでContextオブジェクトから値を取得し、変数messageに格納します。 const message = useContext(ContextObj); messageにはProviderコンポーネントにてvalueに指定された値を受け取ります。 const message = 'React useContext'; export default function App() { return ( <> <ContextObj.Provider value={message}> <Consumer /> </ContextObj.Provider> </> ); }; すると画面に「React useContext」が表示されると思います。 次はファイルを分割した場合を試してみたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む