【Next.js】Head内に要素を組み込む(外部スクリプトとか)
Next.jsでHead内にCSSや外部script、titleタグなどを組み込みたかったので調べてみました。
結論としては、
import Head from 'next/head'
をインポートし、
render関数内などに、
<Head>
<script src="https://js.stripe.com/v2/"></script>
</Head>
こんな感じでHeadタグを作って、組み込みたい要素を入れてあげるだけでOKです!
【Firebase Functions】functions@: The engine "node" is incompatible with this module. Expected version "8". Got "10.16.3"エラーが吐き出される
Next.js内でFirebase Functionsを使用するためにyarn serveを行うと以下のようなエラーが。
error functions@: The engine "node" is incompatible with this module. Expected version "8". Got "10.16.3" error Commands cannot run with an incompatible environment.
Firestoreが求めているnodeのバージョンが8なのですが、
実際に使用されているローカルのnodeのバージョンが10.16.3ですよ、というエラーでした。
nodeを8にダウングレードすると他のプロジェクトに影響が出るかもしれないということでanyenv、nodenvを入れてみたのですが正しく作動しなかったため以下のような対策をしました。
functions/package.json
"engines": { "node": "8" },
上記、プロジェクト直下のfunctions/package.jsonを以下のように改変
"engines": { "node": "10" },
これでnode10で動くようになりました。
【Laravel5.8】ログアウトのredirect先をカスタマイズしたい
Laravel5.8のmake:authで作成したログアウト機能で、redirect先をカスタマイズしたい際の方法です。
/magotaku/app/Http/Controllers/Auth/LoginController.php
<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; // 追加 class LoginController extends Controller { /* |-------------------------------------------------------------------------- | Login Controller |-------------------------------------------------------------------------- | | This controller handles authenticating users for the application and | redirecting them to your home screen. The controller uses a trait | to conveniently provide its functionality to your applications. | */ // 書き換え use AuthenticatesUsers { logout as performLogout; } /** * Where to redirect users after login. * * @var string */ protected $redirectTo = '/home'; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest')->except('logout'); } // logoutファンクションを作成 public function logout(Request $request) { $this->performLogout($request); // redirect先は人それぞれカスタマイズ return redirect('/home'); } }
以上です。
参考
qiita.com
【Next.js】FirebaseのAuthenticationで登録したユーザー情報を描画する
前回の記事では、Next.jsにFirebaseのAuthenticationを使ってユーザー登録機能を追加しました。
nekorokkekun.hatenablog.com
そこで「登録したユーザーの情報を動的に描画するマイページ」を作りましょう。
前提
先ほどもご紹介した前回の記事でログイン機能とHOCの実装まではコピペでも構わないので済ませておいてください。
nekorokkekun.hatenablog.com
ユーザー情報の取得
とは言っても書き方はとても簡単です。
pages/dashboard.js
import React from 'react'; import withAuth from "../src/helpers/withAuth"; // firebaseをimport import { firebase, auth } from "../src/firebase"; class Dashboard extends React.Component { render() { // firebase.auth().currentUserでログインユーザーの情報が取得可能 const user = firebase.auth().currentUser; return( <div> <h2>アカウント情報</h2> <h4>アカウント名</h4> {user.displayName} <h4>メールアドレス</h4> <p> {user.email} </p> <img src={user.photoURL} /> </div> ) } } export default withAuth(Dashboard);
これで以下のような画面が表示されたはずです。
firebase.auth().currentUserの中身
firebase.auth().currentUserでログインユーザーの情報を取得しています。
中身はオブジェクト型になっており、先ほどのコードに登場した「displayName」や「email」「photoURL」なども全て登録したユーザーの情報から引き出されています。
他にも色々な情報が入っていますので、必要に応じて取得すると良いでしょう。
【Next.js】Firestoreから任意のデータを取得して描画する
今回はNext.jsでFirebaseから任意のデータを取得して描画する方法について解説をしていきます。
ウェブアプリを作成する際、レイアウトは決めておいてコンテンツの名前や説明文などはデータを取得してレイアウトにはめ込みたい、という場合がありますね。
このように動的なページを作成するための方法を以下で紹介します。
前提
Firebase Cloud Firestore保存したデータを取得することになります。
すでにFirebaseでプロジェクトが作成されており、Firestoreにデータがいくつか登録されているという前提で進みます。
まだFirebaseのプロジェクト作成やデータの登録ができていないという場合は以下の記事を読み、
- Firebaseでプロジェクトの作成
- データの登録
- Next.jsプロジェクトの作成
- FirebaseとNext.jsプロジェクトのDB接続
を済ませておいてください。
nekorokkekun.hatenablog.com
nekorokkekun.hatenablog.com
また上記ではわざわざデータ登録するためにUIを作成していますが、プロジェクトを作成後、サイドメニューからDatabaseを選ぶことでブラウザからコレクション・ドキュメントの登録(=データの登録)を行うことができます!
Firestoreの確認
まずはFirestoreにデータが保存できるかどうかの確認をしましょう。
ブラウザ上でFirebaseのコンソールからプロジェクト>サイドメニューのDatabaseで確認できます。
実際に見ると以下のようにデータが登録されていることがわかります。
FirestoreはNoSQLなのでMySQLなどのようにテーブル、カラム、レコードといったものはないのですが、イメージとしては
- コレクション = テーブル
- ドキュメント = レコード
- データ = カラムと実際のデータ
と考えるとしっくり方も多いかもしれません。
Firestoreでは以下のようなイラストで図解を出してくれています。
データの一覧表示
まずは先程確認したデータを一覧表示できるページを作成しましょう。
pages/index.js
import { db } from '../lib/db'; import React from 'react'; import Link from 'next/link'; export default class Index extends React.Component { static async getInitialProps() { let result = await db.collection('fanPages') .get() .then(snapshot => { let data = [] snapshot.forEach((doc) => { data.push( Object.assign({ id: doc.id }, doc.data()) ) }) return data }).catch(error => { return [] }) return {datas: result} } render() { const firestoreDatas = this.props.datas return ( <div> <h3>Firestoreのデータ一覧</h3> <div> <ul> {firestoreDatas.map(fanPage => <li key={fanPage.id}> <Link href="/p/[detailid]" as={`/p/${fanPage.id}`}> <a>{fanPage.artistName}</a> </Link> </li> )} </ul> </div> </div> ); } }
これで以下のような画面が表示されたのではないでしょうか。
長いのでいくつかに分けて解説をします。
getInitialPropsでFirestoreからデータの取得
static async getInitialProps() { let result = await db.collection('fanPages') .get() .then(snapshot => { let data = [] snapshot.forEach((doc) => { data.push( Object.assign({ id: doc.id }, doc.data()) ) }) return data }).catch(error => { return [] }) return {datas: result} }
getInitialPropsはクラスコンポーネントで使用できるメソッドです。
コンポーネント生成時にpropsを自動取得してくれるという便利なメソッドなので覚えておきたいですね。
肝心の中身も見ていきましょう。
Firestoreからデータ取得するコレクションを指定
static async getInitialProps() { let result = await db.collection('fanPages') .get() .then(
まず「db.collection('fanPages')」でコレクション「fanPages」からデータ取得を行うことを宣言します。
「db」は変数で、Firestoreの接続設定を他ファイルから取得済みです。詳しくはこちらをご覧ください。
また上記のコードでは非同期処理も行なっています。
「async」と「await」が書かれている部分ですね。
重要なのは「await」を宣言する場所で、意味としては
「result以降の処理が終わるまで結果をreturnしないでね」
ということになります。
Firestoreからのデータ取得は時間が掛かりますので、awaitを配置しておかなければ、データ取得前に「result」がreturnされてしまうため、結果「空っぽのresult」しか返ってこないということになります。
Firestoreのデータを取得して配列に入れる
snapshot => { let data = [] snapshot.forEach((doc) => { data.push( Object.assign({ id: doc.id }, doc.data()) ) }) return data
snapshotとは?
snapshot => {
こんな1行がありますが、「snapshot」とはFirestoreのその「瞬間」のデータが格納されているものです。
データ取得の際に写真を取るイメージで、その瞬間のデータをsnapshotと名付けているんですね。
データをドキュメントごとに配列に収める
data.push( Object.assign({ id: doc.id }, doc.data())
snapshotの1行上で「let data = []」と、空の配列を用意しました。
その後、Object.assignでdataの中にオブジェクト形式でFirestoreのデータをドキュメントごとに格納しています。
実際、この状態のdataをconsole.logで見てみると以下のように出力されています。
[ { id: 'JNTwv1d5W0g1yXQr667j', artistName: 'test1', body: 'test1', category: 'singer', monthlyFee: '1000', pageName: 'test1' }, { id: 'aW749fTXcm99meV7x2WB', artistName: 'test2', body: 'test2', category: 'calligrapher', monthlyFee: '2000', pageName: 'test2' } ]
一番外側に来ているがdata = で定義した配列ですね。
そして、その中にドキュメントごとにオブジェクト型に整形されたFirestoreのデータが格納されていることがわかります。
ここで配列にわざわざ格納するのは、後ほどmap関数で展開するためです。
そして最後に「return data」で中身の入ったdataが「let result」で定義した「result」の中に入りました。
エラーの場合は空の配列を返す
}).catch(error => { return [] })
もしFirestoreからのデータ取得の際にエラーが発生した場合には、「return []」で空の配列をresultに格納する設定にしています。
resultを返してpropsとして扱えるようにする
return {datas: result}
先程までの処理でデータがdata配列に入り、その配列がresultという変数に格納されました。(エラーの場合は空のdata配列がresult変数に格納されています。)
そのresultをpropsである「datas」に渡します。(名前は任意です)
こうすることでコンポーネント内でpropsとして格納したFirebaseのデータを扱うことができるようになるというわけですね。
render関数で取得したデータを描画
render() { const firestoreDatas = this.props.datas return ( <div> <h3>Firestoreのデータ一覧</h3> <div> <ul> {firestoreDatas.map(fanPage => <li key={fanPage.id}> <Link href="/p/[detailid]" as={`/p/${fanPage.id}`}> <a>{fanPage.artistName}</a> </Link> </li> )} </ul> </div> </div> ); } }
次にrender関数で取得したデータを描画しています。こちらも細かく分けてみていきますね。
propsを定数に格納
const datas = this.props.datas
先程getInitialPropsで返されたpropsは「this.props.datas」という呼び方で参照する(props値を使用する)ことができます。
しかし名称として長いため、「datas」という定数に置き換えています。
map関数でpropsを展開
{datas.map(fanPage => // 省略 )}
map関数は配列の中身を展開することができるメソッドです。
先程dataをconsole.logで確認した際を思い出してください。
[ { id: 'JNTwv1d5W0g1yXQr667j', artistName: 'test1', body: 'test1', category: 'singer', monthlyFee: '1000', pageName: 'test1' }, { id: 'aW749fTXcm99meV7x2WB', artistName: 'test2', body: 'test2', category: 'calligrapher', monthlyFee: '2000', pageName: 'test2' } ]
このようになっていました。この中身をオブジェクトごとに1つずつ展開することができるのです。
今はオブジェクト型データが2つだけしか入っていないためあまりメリットは感じられませんが、大量のデータを処理する際にはとても便利な関数です。
そして「datas.map(fanPage」と書くことによって、datasの中のオブジェクトを1つずつ「fanPage」という変数に格納します。
foreachでいうところの「foreach 配列 as 変数」という書き方に似ていますね。
mapで展開されたデータから値を参照
<li key={fanPage.id}> <Link href="/p/[detailid]" as={`/p/${fanPage.id}`}> <a>{fanPage.artistName}</a> </Link> </li>
上記のような書き方でリストレンダリングすることができます。
{ id: 'JNTwv1d5W0g1yXQr667j', artistName: 'test1', body: 'test1', category: 'singer', monthlyFee: '1000', pageName: 'test1' },
上記のようにオブジェクトにデータが入っているため、「fanPage.id」「fanPage.body」などといった書き方で個別にデータを参照することができるのです。
Linkの書き方について
<Link href="/p/[detailid]" as={`/p/${fanPage.id}`}>
Linkの書き方にも注目しましょう。
hrefでは実際にpages/p/[detailid].jsというファイルにアクセスするということが明示されています。しかし実際にユーザーがURLで確認できるのは「/p/JNTwv1d5W0g1yXQr667j」などといった独自URLです。
このような書き方をすることによって動的なページを作成することができるようになっています。
動的なページの作成
次にURLごとにページの中身が切り替わる動的なページを作成していきます。
pages/p/[detail].jsを作成しましょう。
import { db } from '../../lib/db'; import React from 'react'; export default class Detail extends React.Component { static async getInitialProps({query}) { let result = await db.collection("fanPages") .doc(query.detail) .get() .then(function(doc) { if (doc.exists) { return doc.data(); } else { console.log('not exists'); } }).catch(error => { console.log(error) return [] }) return {detail: result} } render() { const detail = this.props.detail; return ( <React.Fragment> <div> <h1>{detail.artistName}</h1> <p> {detail.body} </p> <ul> <li>{detail.category}</li> <li>{detail.monthlyFee}</li> <li>{detail.pageName}</li> </ul> </div> </React.Fragment> ); } }
これで以下のような画面が出てきたのではないでしょうか。
こちらも分けて解説をしていきます。
getInitialPropsでURLクエリパラメータから得たIDを元にデータを取得
まずはURLクエリパラメータから渡ってきたデータIDを元に、Firestoreから任意のデータを取得します。
先程扱ったgetInitialPropsと異なる点が何点かありますね。
getInitialPropsには引数がある
実はgetInitialPropsには動的なページ作成に必要な情報を引数として自動的に生成してくれる機能があります。
static async getInitialProps({query}) {
上記の例では「query」という引数を受け取っています。
このqueryをconsole.logで見てみると…
{ detail: 'JNTwv1d5W0g1yXQr667j' }
と出力されました。
URLクエリパラメータでも使用した一意のデータIDですね。
このデータIDだけにアクセスしたければ「query.detail」と書いてあげればOKですね。
他にもgetInitialPropsには様々な引数があります。
Next.jsのドキュメントには以下のような記述がありました。
nitialProps receives a context object with the following properties:
pathname - path section of URL
query - query string section of URL parsed as an object
asPath - String of the actual path (including the query) shows in the browser
req - HTTP request object (server only)
res - HTTP response object (server only)
err - Error object if any error is encountered during the rendering
Documentation - Getting Started | Next.js
console.logで中を見てみるとかなり膨大な量のデータを引数で受け取れるということがわかりますので、こちらも検証してみてくださいね。
ドキュメントを指定したデータの取得
db.collection("fanPages") .doc(query.detail) .get() .then(function(doc) { if (doc.exists) { return doc.data(); } else { console.log('not exists'); }
先程はコレクションのみの指定でしたが、さらにデータIDを引数にしてドキュメントを指定することができます。
MySQLで言う所の
Select * from fanPages where id = 1;
の「where」以降の条件付けとでも言えばいいでしょうか。
whereがなければテーブル(コレクション)全てのデータを取得することとなり、それは先程行った全件取得と同じですね。
そしてドキュメントの指定をする場合には「.doc(query.detail)」といった書き方が必要です。引数の「query.detail」には先程説明した通り、一意のデータIDが入っています。
後は先ほど説明したgetInitialPropsとほとんど変わりませんので割愛します。
render関数で描画
const detail = this.props.detail; return ( <React.Fragment> <div> <h1>{detail.artistName}</h1> <p> {detail.body} </p> <ul> <li>{detail.category}</li> <li>{detail.monthlyFee}</li> <li>{detail.pageName}</li> </ul> </div>
propsで受けとったデータは再度、定数に格納して「detail.artistName」といった形で参照可能です。
これでFirestoreから任意のデータを取得して描画することができました。お疲れ様でした!
【Next.js】Firebase AuthenticationとHOCを使用してサインイン機能を実装する
本記事では、Next.jsプロジェクトにFirebase Authenticationを組み込み、サインイン機能を実装するとともに、HOC(高階コンポーネント)を使用してサインインユーザーのみが閲覧できるページを作成していきます。
- Next.jsプロジェクトの準備
- サインインユーザーのみ見られるコンポーネントの作成
- 必要となるパッケージやDB接続ファイルの準備
- FirebaseでAuthentication設定
- index.jsの書き換え
- HOC(高階コンポーネント)の作成
- dashboard.jsへ追記
- index.jsに追記
- サインインをしてみる
Next.jsプロジェクトの準備
まずはNext.jsプロジェクトの準備をしていきましょう。
npx create-next-app next-firebase-auth cd next-firebase-auth yarn devhttp://localhost:3000/
http://localhost:3000/にアクセスしてみると以下のような画面が出てくるはずです。
サインインユーザーのみ見られるコンポーネントの作成
次にサインインユーザーのみが見ることのできるコンポーネントを作成していきましょう。
cd pages touch dashboard.js
pages/dashboard.js
import React from 'react'; import Nav from '../components/nav'; class Dashboard extends React.Component { render() { return ( <div> <Nav /> <h1>Dashboard Page</h1> <p>You can't go into this page if you are not authenticated.</p> </div> ) } } export default Dashboard;
まだHOCに設定していないためアクセス可能です。
必要となるパッケージやDB接続ファイルの準備
次にFirebaseの接続に必要となるパッケージやDB接続用ファイルの準備をしていきます。
まずはルートディレクトリに.envファイルを作成します。
touch .env
ここにFirebaseでプロジェクトを作成した際に発行された
たちを定数として収めていきます。
Firebaseにおけるプロジェクトの作成から上記のキーの発行手順については以下の記事に書かれています。
nekorokkekun.hatenablog.com
取得したキーたちを以下のように.envに書き込んでいきましょう。
.env
FIREBASE_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_AUTH_DOMAIN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_DATABASE_URL=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_PROJECT_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_STORAGE_BUCKET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_MESSAGING_SENDER_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FIREBASE_APP_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
次にnext.config.jsをルートディレクトリ直下に作成します。
touch next.config.js
next.config.js
require("dotenv").config(); const path = require("path"); const Dotenv = require("dotenv-webpack"); module.exports = { webpack: config => { config.plugins = config.plugins || []; config.plugins = [ ...config.plugins, // Read the .env file new Dotenv({ path: path.join(__dirname, ".env"), systemvars: true }) ]; return config; } };
最後にFirebaseへ接続するためのファイルを作成しましょう。
// ルートディレクトリから mkdir src cd src mkdir firebase cd firebase touch index.js
src/firebase/index.js
import firebase from "firebase/app"; import "firebase/auth"; const config = { apiKey: process.env.FIREBASE_API_KEY, authDomain: process.env.FIREBASE_AUTH_DOMAIN, databaseURL: process.env.FIREBASE_DATABASE_URL, projectId: process.env.FIREBASE_PROJECT_ID, storageBucket: process.env.FIREBASE_STORAGE_BUCKET, messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, appId: process.env.FIREBASE_APP_ID }; if (!firebase.apps.length) { firebase.initializeApp(config); } const auth = firebase.auth(); export { auth, firebase };
これでprocess.env.〇〇(定数名)という形でプロジェクト内の他ファイルから.envで設定した定数名を使用するための下準備ができました!
そして以下の2つのパッケージをインストールすることで.envで設定した定数がprocess.env.〇〇(定数名)といった形でプロジェクト内の他のファイルからアクセス可能となります。
yarn add firebase dotenv yarn add dotenv-webpack
FirebaseでAuthentication設定
次にFirebaseのコンソール上からAuthenticationの設定をしておきましょう。
先ほど作成したFirebaseのプロジェクトにアクセスします。
そしてサイドメニューの「Authentication」をクリックしましょう。
画像の順番に従い、
① タブメニューの「ログイン方法」をクリック
② 「有効にする」をオン
③ プロジェクトのサポートメールを設定
という順序で設定を完了させましょう。
index.jsの書き換え
独自のUIにしていますが、ポイントはfirebaseからauthをimportしているというところです。
import React from "react"; import Link from "next/link"; import Nav from "../components/nav"; import { auth, firebase } from "../src/firebase"; import Head from "next/head" class Home extends React.Component { render() { return ( <div> <Head title="Home" /> <Nav /> <div className="hero"> <h1 className="title"> Welcome to Firebase Authentication in Next.js! </h1> <p className="description"> Click on the Dashboard link to visit the dashboard page. </p> <div className="row"> <Link href="/dashboard"> <a className="card"> <h3>Go to Dashboard→</h3> <p>Visit Dashboard</p> </a> </Link> </div> </div> <style jsx>{` .hero { width: 100%; color: #333; } .title { margin: 0; width: 100%; padding-top: 80px; line-height: 1.15; font-size: 48px; } .title, .description { text-align: center; } .row { max-width: 880px; margin: 80px auto 40px; display: flex; flex-direction: row; justify-content: space-around; } .card { padding: 18px 18px 24px; width: 220px; text-align: left; text-decoration: none; color: #434343; border: 1px solid #9b9b9b; } .card:hover { border-color: #067df7; } .card h3 { margin: 0; color: #067df7; font-size: 18px; } .card p { margin: 0; padding: 12px 0 0; font-size: 13px; color: #333; } `}</style> </div> ); } } export default Home;
HOC(高階コンポーネント)の作成
いよいよHOC(高階コンポーネント)を作成します。
高階コンポーネントについて今回は詳しく解説しませんが、「通常のコンポーネントに機能を追加するもの」という認識でOKです。
今回のHOCで追加する機能が「サインイン後に見ることができる」というものですね。
// src直下で mkdir helpers cd helpers touch withAuth.js
src/helpers/withAuth.js
import React from "react"; import router from "next/router"; import { auth } from "../firebase"; const withAuth = Component => { return class extends React.Component { constructor(props) { super(props); this.state = { status: "LOADING" }; } componentDidMount() { auth.onAuthStateChanged(authUser => { if (authUser) { this.setState({ status: "SIGNED_IN" }); } else { router.push("/"); } }); } renderContent() { const { status } = this.state; if (status == "LOADING") { return <h1>Loading ......</h1>; } else if (status == "SIGNED_IN") { return <Component {...this.props} />; } } render() { return <>{this.renderContent()}</>; } }; }; export default withAuth;
こちらは細かく解説していきましょう。
引数でComponentを受ける
const withAuth = Component => {
後ほど「サインイン後しか見られないコンポーネント」を設定する際に
export default withAuth(コンポーネント名)
という書き方でexportします。
この「コンポーネント名」で指定されたコンポーネントが、withAuthの引数に設定された「Component」を指しています。
ユーザー認証されるまではstateで読み込みが「Loading」
constructor(props) { super(props); this.state = { status: "LOADING" }; }
stateの状態は基本的に「LOADING」(=見認証の状態)にしておき、
ユーザー認証されて初めてstateが「SIGNED IN」の状態となります。
authUserで認証の可否を判別
componentDidMount() { auth.onAuthStateChanged(authUser => { if (authUser) { this.setState({ status: "SIGNED_IN" }); } else { router.push("/"); } }); }
サインインが成功した場合、「authUser」インスタンスの中には、ユーザー情報が入ることになります。そうするとif文でstateのstatusが「SIGNED_ID」になり、コンテンツを見ることができるようになります。
uid(=ユーザーID)やメールアドレスなどが入っているため、サインイン後にも利用できそうですね!
もしユーザーが未認証の場合にはauthUserは空のままのため、elseの方に行き、そのままトップページに遷移することとなります。
statusがLOADINGのままだと、LOADINGという画面が表示されることとなります。それが以下のものですね。
renderContent() { const { status } = this.state; if (status == "LOADING") { return <h1>Loading ......</h1>; } else if (status == "SIGNED_IN") { return <Component {...this.props} />; } }
そしてstatusがSIGNED_INだと、HOCでサインインユーザーのみが見られるComponentを表示できるようになっています。
dashboard.jsへ追記
先ほど作成したdashboard.jsにwithAuth.jsを合体させる形で追記しましょう。
pages/dashboard.js
import React from 'react'; import Nav from '../components/nav'; // 追加 import withAuth from "../src/helpers/withAuth"; class Dashboard extends React.Component { render() { return ( <div> <Nav /> <h1>Dashboard Page</h1> <p>You can't go into this page if you are not authenticated.</p> </div> ) } } // withAuthの引数にdashboard.jsを追加 export default withAuth(Dashboard);
これでサインインユーザーのみがdashboard.jsを見られるような設定となりました!
index.jsに追記
次にサインインなどの処理を行うためのコードをpages/index.jsに追記します。
pages/index.js
import React from "react"; import Link from "next/link"; import Nav from "../components/nav"; import { auth, firebase } from "../src/firebase"; import Head from "next/head" class Home extends React.Component { // ここから下を追加 handleSignIn = () => { var provider = new firebase.auth.GoogleAuthProvider(); provider.addScope("https://www.googleapis.com/auth/contacts.readonly"); auth .signInWithPopup(provider) .then(() => { alert("You are signed In"); }) .catch(err => { alert("OOps something went wrong check your console"); console.log(err); }); }; handleSignout = () => { auth .signOut() .then(function() { alert("Logout successful"); }) .catch(function(error) { alert("OOps something went wrong check your console"); console.log(err); }); }; // ここまでを追加 render() { return ( <div> <Head title="Home" /> <Nav /> <div className="hero"> <h1 className="title"> Welcome to Firebase Authentication in Next.js! </h1> <p className="description"> Click on the Dashboard link to visit the dashboard page. </p> <div className="row"> <Link href="/dashboard"> <a className="card"> <h3>Go to Dashboard→</h3> <p>Visit Dashboard</p> </a> </Link> {/* ここから下2行を追加 */} <button onClick={this.handleSignIn}>Sign In using google</button> <button onClick={this.handleSignout}>Signout</button> </div> </div> <style jsx>{` .hero { width: 100%; color: #333; } .title { margin: 0; width: 100%; padding-top: 80px; line-height: 1.15; font-size: 48px; } .title, .description { text-align: center; } .row { max-width: 880px; margin: 80px auto 40px; display: flex; flex-direction: row; justify-content: space-around; } .card { padding: 18px 18px 24px; width: 220px; text-align: left; text-decoration: none; color: #434343; border: 1px solid #9b9b9b; } .card:hover { border-color: #067df7; } .card h3 { margin: 0; color: #067df7; font-size: 18px; } .card p { margin: 0; padding: 12px 0 0; font-size: 13px; color: #333; } `}</style> </div> ); } } export default Home;
こちらも少し解説をします。
以下はサインイン・サインアウトの処理を行うイベントの設定です。
handleSignInでは、ボタンを押すとサインインモーダルウィンドウがポップアップし、サインイン処理ができるという流れになります。
こちらは私たちが作り込む必要はありません。
handleSignIn = () => { var provider = new firebase.auth.GoogleAuthProvider(); provider.addScope("https://www.googleapis.com/auth/contacts.readonly"); auth .signInWithPopup(provider) .then(() => { alert("You are signed In"); }) .catch(err => { alert("OOps something went wrong check your console"); console.log(err); }); };
handleSignoutも同じくで、Signout()メソッドを使えば簡単にユーザーを未承認の状態に戻してくれます。
handleSignout = () => { auth .signOut() .then(function() { alert("Logout successful"); }) .catch(function(error) { alert("OOps something went wrong check your console"); console.log(err); }); };
そして、上記2つのイベントを呼び出すのが以下のbuttonですね。
<button onClick={this.handleSignIn}>Sign In using google</button> <button onClick={this.handleSignout}>Signout</button>
【Next.js】Firestoreのデータを削除する
今回はNext.jsからFirebase Cloud Firestoreのデータを削除する方法について解説していきます。
以下の記事を前提にしています。
nekorokkekun.hatenablog.com
nekorokkekun.hatenablog.com
コードの全体像
まずは今回のコードの全体像を見ていきましょう。
import { db } from '../lib/db'; import React from 'react' export default class Posts extends React.Component { static async getInitialProps() { let result = await new Promise((resolve, reject) => { db.collection('posts') .get() .then(snapshot => { let data = [] snapshot.forEach((doc) => { data.push( Object.assign({ id: doc.id }, doc.data()) ) }) resolve(data) }).catch(error => { reject([]) }) }) return {posts: result} } handleDelete = (id) => { db.collection('posts') .doc(id) .delete() .then(function() { console.log("Document successfully deleted!"); }).catch(function(error) { console.error("Error removing document: ", error); }); } render() { const posts = this.props.posts return ( <React.Fragment> {posts.map(post => <div className="post" key={post.id}> <h2> {post.title} </h2> <p> {post.body} </p> <button onClick={this.handleDelete.bind(this, post.id)}>削除</button> </div> )} <style jsx>{` .post { width: 40%; border: 1px solid black; background-color: gray; margin-bottom: 10px; } `}</style> </React.Fragment> ); } }
一意のidを取得する
削除する際に「どのデータを削除するのか」を識別するための一意のidを取得する必要があります。
こちらの記事で述べている通り、Firestoreにデータを登録する際、setで任意のidを設定するか、addで自動的に一意のidを作成しているかと思います。
そのidを特定するためには以下のような記述が必要です。
{posts.map(post => <div className="post" key={post.id}> <h2> {post.title} </h2> <p> {post.body} </p> <button onClick={this.handleDelete.bind(this, post.id)}>削除</button> </div> )}
map関数で取得したFirestoreの全データを1つずつ展開しています。
中でも以下の記述に着目してほしく、
<button onClick={this.handleDelete.bind(this, post.id)}>削除</button>
後ほど解説するhandleDeleteというイベントにbindしてpost.idを付随させています。
「post.id」が一意のidということになり、それをイベントにbindさせているということです。
また、onClickにhandleDeleteイベントを指定することによって、クリックすると該当のidのデータを削除する、という運びになります。
削除イベントの作成
handleDelete = (id) => { db.collection('posts') .doc(id) .delete() .then(function() { console.log("Document successfully deleted!"); }).catch(function(error) { console.error("Error removing document: ", error); }); }
こちらが指定したidのデータを削除するためのイベントです。
delete関数で指定したidを持つデータが削除されるようになっています。
詳しくは公式ドキュメントを参照してください。
これで該当のデータを削除ボタンで削除できるようになりました!