TL;DR: 要約:本書では、ポップアップ機能を使ってAuth0認証のReactアプリケーションを構築していきます。Reactの最新機能についても学んでいきますが、create-react-app のアプリから始めましょう。クラスは使わずにReact Hooksを使ってコンポーネントを書き込んでいきます。Auth0の ポップアップ認証フローも使い、ポップアップモードがプロジェクトに最適なオプションになる理由も学んでいきます。GitHubレポジトリはこちらをご覧ください。

Reactの新規機能

React Hooks は現在のバージョンがReact v16.8.0-alpha.0で新しくなります。これで、機能コンポーネントが状態を持つことができます。状態を持つためにクラスコンポーネントが不要になります。ではHooks について詳しく学んでいきましょう。

「React Hooksは現在のバージョンがReact v16.8.0-alpha.0で新しくなります。これで、機能コンポーネントが状態を持つことができます。」

React Hooks

HooksはReactの新しく追加されたもので、状態を使うのにクラスコンポーネントの書き込みを避けることができます。機能コンポーネントはずっと強力で少ないコードを呼び出します。やった!フックを使って状態を機能コンポーネントに「フック」して汎用性をずっと高めましょう。

Hooksを使ってフロントエンド コンポーネントを書いていきます。アプリケーションのコードについて学ぶとき、この新しいReact機能をどのようにして実装するかについて説明します。

Auth0のポップアップ認証

Auth0 ではユーザーがログインするために多様でさまざまな方法を提供しています。今回の例では、ポップアップ方法 を使っていきます。ユーザーはポップアップを有効にするためにボタンをクリックしてログインし、それからポップアップは認証の後に非表示になります。

多くのユーザーはブラウザー上のポップアップを無効にするので、使う必要がないときはポップアップ方法に再分類しないことをお勧めします。その機能を確認するのは良い事です!

今日構築するもの

今回は バックエンドAPIでシングルページ アプリケーションを構築していきます。初期ビューは Log In ボタンが付いているテレビ番組のリストです。ユーザーが Log In ボタンをクリックすると、Auth0ログイン ポップアップが表示されます。

ログインが成功したら、そのポップアップは非表示になり、ビューは映画やテレビ番組のリストに変わります。映画やテレビ番組のビューは保護されていますので、ユーザー認証が正しく行われない限り表示されません。これらリストの下部には Log Out ボタンがあり、テレビ番組だけの保護されていないビューが表示されます。

Reactの使い方

今回はReactプロジェクトをスキャフォールディングするcreate-react-appコマンドを使います。新規プロジェクトを保存したい場所に移動して次のコマンドをタイプします。

npx create-react-app react-auth0-popup

このコマンドはcreate-react-appだけでなくnpxも取得し、パッケージをローカルにインストールする作業が不要になり、最新バージョンに取り込みます。npxを使用するためには、NPM 5.2またはそれ以降がインストールされている必要があります。ひとつのコマンドで両方の作業を処理する素晴らしい機能です。その構築が終わったら、プロジェクトに進みます。

cd react-auth0-popup

使用するIDEでプロジェクトを開き、プロジェクトが正しく実行されているかを見ましょう。次のコマンドを実行します。

npm run start

それから自動的にブラウザーのlocalhost:3000 に移動します。ここでReactロゴが表示されます!

「create-react-appとnpxを一緒に使ってReactアプリをスキャフォールディングするのは素晴らしいです!」

Auth0 を設定する

今回はAuth0を使ってアプリケーションの認証を行います。新規アカウントを作成するには、Auth0 のサインアップ ページに移動Auth0 のサインアップ ページに移動します。既存アカウントにログインするには、Auth0 のホームページに移動します。ログインが終わったら、右上にある + New Application という大きなボタンをクリックします。

すると、モーダルが表示され、新規アプリケーションに「react-auth0-popup」と名付けます。Single Page Web Appをクリックして、Createをクリックします。アプリケーションに使用するテクノロジにはReactオプションをクリックします。後ほどAuth0をプロジェクトに統合する方法について説明していきます。

クイック スタートの方法がありますが、そのクイック スタートを使わないAuth0実装についても説明します。

Our Auth0 API

次に、プロジェクトのAPIを設定します。これはアプリの「対象ユーザー」面になります。

audienceパラメーターについての詳細情報はこちらでご覧ください。

Auth0 APIダッシュボードに移動します。そこから、「作成」ボタンをクリックします。

Create API button on Auth0

名前と識別子をタイプし、「作成」をクリックします。

nameの例として「tvshows-movie-api」、identifierの例として「https://tvmoviesapi」が挙げられます。識別子は一度作成したら修正できないので、ご注意ください。

そこから、「設定」タブをクリックすると、識別子が表示され、名前欄には入力した情報が表示されます。この識別子は後ほど使いますので、現時点では使いやすいようにしておいてください。

SPAのAPIデータ

コードに戻って、APIを設定しましょう。プロジェクトのルートに apiフォルダーを作成します。そのフォルダー内にdataフォルダーを作成します。ここには2つの別のファイルがあります。ひとつは「テレビ番組」リストのデータ、もうひとつは「映画」リストのデータです。tvShows.jsonというタイトルのdataフォルダーにファイルを作ります。オブジェクトの配列を作ります。どの番組、データを使っても構わないので、ここでは6つの異なるテレビ番組についての6つのオブジェクトのリストがあります。次のように表示されます。

// api/data/tvShows.json
// コピーして貼り付けするときは必ずjsonファイルのコメントアウト ファイルのパスを消去してください。

[
  {
    "id": 1,
    "name": "The Office",
    "airDate": "March 24, 2005",
    "lastShowDate": "May 16, 2013",
    "aCastMember": "Steve Carell",
    "totalSeasons": "9",
    "totalEpisodes": "201",
    "interestingFact": "John Krazinski shot the footage of Scranton that appears in the show’s opening credits from his jeep when he and a few friends cased the city before he began shooting."
  },
  {
    "id": 2,
    "name": "Mad Men",
    "airDate": "July 19, 2007",
    "lastShowDate": "May 17, 2015",
    "aCastMember": "Jon Hamm",
    "totalSeasons": "7",
    "totalEpisodes": "92",
    "interestingFact": "The first and second episodes were shot one year apart."
  },
  {
    "id": 3,
    "name": "Friends",
    "airDate": "September 22, 1994",
    "lastShowDate": "May 6, 2004",
    "aCastMember": "Jennifer Aniston",
    "totalSeasons": "10",
    "totalEpisodes": "236",
    "interestingFact": "The theme song performed by The Rembrandts became a #1 single on the American pop charts."
  },
  {
    "id": 4,
    "name": "Stranger Things",
    "airDate": "July 15, 2016",
    "lastShowDate": "October 27, 2017",
    "aCastMember": "Millie Bobby Brown",
    "totalSeasons": "2",
    "totalEpisodes": "17",
    "interestingFact": "They auditioned 906 boys and 307 girls for the main roles."
  },
  {
    "id": 5,
    "name": "Seinfeld",
    "airDate": "July 5, 1989",
    "lastShowDate": "May 14, 1998",
    "aCastMember": "Julia Louis-Dreyfus",
    "totalSeasons": "9",
    "totalEpisodes": "180",
    "interestingFact": "Jerry says 'Hello, Newman' only 15 times in the entire series."
  },
  {
    "id": 6,
    "name": "Breaking Bad",
    "airDate": "January 20, 2008",
    "lastShowDate": "September 29, 2013",
    "aCastMember": "Aaron Paul",
    "totalSeasons": "5",
    "totalEpisodes": "62",
    "interestingFact": "Only two actors appear in every single episode, Aaron Paul and Bryan Cranston."
  }
]

その同じdataフォルダーで、映画データを保留するオブジェクトの配列があるmovies.jsonというタイトル付きファイルを作ります。再度、6つのオブジェクトのリストにします。データは次のように情報を保留します。

// api/data/movies.json

[
  {
    "id": 1,
    "name": "Forrest Gump",
    "airDate": "July 6, 1994",
    "aCastMember": "Tom Hanks",
    "interestingFact": "During the ping-pong matches, there was no ball; it was entirely CGI, animated to meet the actors’ paddles."
  },
  {
    "id": 2,
    "name": "The Incredibles",
    "airDate": "November 5, 2004",
    "aCastMember": "Samuel L. Jackson",
    "interestingFact": "The original title of the film was 'The Invincibles.'"
  },
  {
    "id": 3,
    "name": "Black Panther",
    "airDate": "January 29, 2018",
    "aCastMember": "Chadwick Boseman",
    "interestingFact": "Chadwick Boseman (Black Panther) has a background in martial arts and did his own stunts."
  },
  {
    "id": 4,
    "name": "Harry Potter and the Sorcerer's Stone",
    "airDate": "November 14, 2001",
    "aCastMember": "Daniel Radcliffe",
    "interestingFact": "The book and movie are called 'Harry Potter and the Philosopher's Stone' everywhere except the United States."
  },
  {
    "id": 5,
    "name": "Finding Nemo",
    "airDate": "May 30, 2003",
    "aCastMember": "Ellen DeGeneres",
    "interestingFact": "The tiki heads in the tank are caricatures of Pixar employees."
  },
  {
    "id": 6,
    "name": "Jurassic Park",
    "airDate": "June 11, 1993",
    "aCastMember": "Jeff Goldblum",
    "interestingFact": "Jurassic Park was shot on location in 1992 on Hawaii's Kauai Island."
  }
]

APIの構成ファイル

apiファイルに、APIルートに必要なデータの一部を保留する構成ファイルを間もなく作ります。apiフォルダーに新規ファイルconfig.jsを作成します。それには、前に設定したAuth0 APIから2つのアイテムがあります。そのファイルは次のように表示されます。

// api/config.js

module.exports = {
  AUTH0_DOMAIN: "yourauth0domain.auth0.com",
  AUDIENCE: "https://yourapifromauth0"
};

注:これら資格情報はAPIアプリケーション設定ではなく、Auth0 ダッシュボード内のアプリケーションにあります。

この設定が終わったら、必ず.gitignoreファイルに移動してこのファイルのパスをそこに入れてください。このファイルを無視してGitHubにプッシュされたら、他者がAuth0情報を取得できないようにします。

APIルート

テレビ番組データや映画データが入っているファイルは各ルートに割り当てなければなりません。apiフォルダーにもうひとつのファイルを作り、それをroutes.jsと名付けます。

このファイル内で使用する次の依存関係を追加する必要があります。

NPMを使ってこれらプロジェクト依存関係をインストールする次のコマンドを実行します。

npm install --save express express-jwt jwks-rsa

これらをインストールしたら、routes.jsファイルのトップにpathと供に保存します。

// api/routes.js

const express = require("express");
const jwt = require("express-jwt");
const jwks = require("jwks-rsa");
const path = require("path");

そのファイルの上部に、テレビ番組および映画のデータ、認証の構成設定をインポートし、エクスプレス ルーターを設定します。

// api/routes.js

// その他のインポート

const allShows = require("./data/tvShows");
const allMovies = require("./data/movies");
const config = require("./config");

const router = express.Router(); 

ここからは認証を確認する機能を構築しましょう。

// api/routes.js

// ここをインポートします

const authCheck = jwt({
  secret: jwks.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: `https://${config.AUTH0_DOMAIN}/.well-known/jwks.json`
  }),
  audience: [config.AUDIENCE],
  issuer: `https://${config.AUTH0_DOMAIN}/`,
  algorithm: "RS256"
});

ルート

ルートパスに追加することで、APIデータのテストができるようになります。Postman のような製品を使うと、http://locahost:3000/api/が「APIが機能している」かどうか、あるいはhttp://localhost:3000/api/data/tvshowsがAPIデータからテレビ番組のリストを返すかどうかのテストができるようになります。

注: /api/data/moviesはミドルウェアauthCheckが問題なく実行するかだけを表示します。

// api/routes.js

// ここをインポートします

router.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "../build/index.html"));
  // When the application is built, this file will be created.
  // It currently is not created.
});

router.get("/api/", (req, res) => {
  res.send("API is working");
});

router.get("/api/data/tvshows", (req, res) => {
  res.json(allShows);
});

router.get("/api/data/movies", authCheck, (req, res) => {
  res.json(allMovies);
});

routes.jsファイルの下部では、そのファイルの下部のルーター情報をエクスポートしてください。

// api/routes.js

// その他のコードはここです

module.exports = router;

SPAのサーバー

これでAPIの用意ができたので、サーバーが機能しているかを確認します。プロジェクトのルートに、serverというタイトルの新規フォルダーを作成します。そのフォルダー内に、server.jsという名のファイルを作成します。ここで、数個の依存関係が必要になるので、そのファイルで作業しましょう。

次の依存関係をサーバー ファイルのプロジェクトにインストールする必要があります。

次のコマンドを実行します。

npm install --save body-parser cors

それらがプロジェクトに追加されているかを確認します。それらのインストールが終わったら、それらをサーバー ファイルに追加して使えるようにしましょう。

server.jsファイルの上部に次の依存関係とルート ファイルのパスを追加したいと思います。また、ポートも宣言します。

// server/server.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const routes = require("../api/routes");

const PORT = 3005;

アプリを設定するには:

// server/server.js

// その他のインポート

const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

app.useを使って、サーバーがあらかじめ定義したルートを使えるようにします。

// server/server.js

app.use("/", routes);

サーバーの設定をしたので、ポートで実行してみましょう。サーバー アプリの下部に次の行を追加します。

// server/server.js

// これはファイルの最後の行です
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));

このファイルをプロジェクト内の端末に保存したら、次のコマンドを実行します。

node server/server.js

または

nodemon

「ポート3005で聞こえる」かを確認します。サーバーが動いています!

アプリケーションを構築する

サーバーを設定し、APIデータが用意できたので、Webブラウザーでこの情報が閲覧できるようにフロントエンドを構築しましょう。

Auth0資格情報

フロントエンド アプリを通して、Auth0資格情報を使っていきます。利便性のため、ファイルの資格情報を保留して必要なコンポーネントに簡単にインポートできるようにします。セキュリティ上の理由から、そのファイルを.gitignoreファイルに含みGitHubにコミットされないようにします。

srcフォルダーにauthフォルダーを作成し、その中にconfig.jsというタイトルのファイルを作ります。ここでは、次のようなものになります。

// src/auth/config.js

export const config = {
  clientId: "your auth0 clientId",
  domain: "yourauth0domain.auth0.com",
  redirect: "http://localhost:3000/close-popup",
    // このリダイレクトについては間もなく説明します。
  logoutUrl: "http://localhost:3000",
  audience: "https://yourauth0api"
};

Auth0資格情報を入力し、そのファイルのパスを.gitignoreファイルに組み込みます。

.gitignoreファイルを開き、この行config.jsを挿入します。これでうまくいきます!ファイルの無視についての詳細情報はこのWebサイトをご覧ください。

アプリケーションの下に前に作った設定タプに移動しましょう。「許可されたコールバックURL」および「許可されたログアウトURL」のフィールドが表示されています。そこにauth/config.jsファイルで入力したURLを入れます。

これはAPI設定からではなく、アプリケーション設定からのものです。

次の情報をアプリケーションの設定セクションに入力します。

「許可されたコールバックURL」-> http://localhost:3000/close-popup「許可されたログアウトURL」-> http://localhost:3000

必ずこの変更を保存してください!

ポップアップを閉じる

ポップアップは用心しなければならないことがあるので、多くの人々はポップアップがブラウザーに表示されないようにブロックしています。Auth0では、ポップアップがブロックされているため、その使用をお勧めしませんが、その使い方を知っておくのはいいことです。

ポップアップを使用するとき、ポップアップの閉じるのを処理する別のファイルが必要です。publicフォルダー内に別のフォルダーを作り、close-popup と名付けます。そこにindex.htmlという名前のファイルを作ります。

ここで、認証が検証された後に「リダイレクト」するようにします。close-popupファイルが実行して、そのポップアップが非表示になって、ユーザーがしていたことに戻るようにします。

このファイル内のコードは次のようです。

// public/close-popup/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta
      charset="utf-8"
      content="font-src: 'self' data: img-src 'self' data: default-src 'self'"
    />
    <title></title>
    <script src="https://cdn.auth0.com/js/auth0/9.8.1/auth0.min.js"></script>
  </head>
  <body>
    <script type="text/javascript">
      const webAuth = new auth0.WebAuth({
        domain: "your auth0 domain",
        clientID: "your client id"
      });
      webAuth.popup.callback();
    </script>
  </body>
</html>

Auth0アプリケーション資格情報をdomainclientIDという2つの行に入れます。domainyourname.auth0.comのようなものでclientIDは長い文字列です。

このファイルを.gitignoreファイルに入れたら、ログイン プロセスが完了した後にポップアップが閉じるようになります。Auth0資格情報(ドメインとclientID)がこのファイル内に示されるので.gitignoreに入れます。

Auth0サービス ファイル

このファイルは認証の不思議が発生する場所です。authフォルダー内にservice.jsファイルを作成します。auth0-js依存関係が必要です。コマンド行内に、次をタイプします。

npm install --save auth0-js

それをインストールしたら、config.jsファイルと新規依存関係をauth/service.jsファイルの上部に次のようにインポートします。:

// src/auth/service.js

import { config } from "./config";
import * as Auth0 from "auth0-js";

Authコンポーネントから始めて、すべての構成項目を入れます。インポートの下に、次のコードを挿入します。

// src/auth/service.js

// ここをインポートします

class Auth {
  auth0 = new Auth0.WebAuth({
    domain: config.domain,
    clientID: config.clientId,
    redirectUri: config.redirect,
    audience: config.audience,
    responseType: "id_token token",
    scope: "openid profile email"
  });

}

responseTypeid_token tokenがあることが分かると思います。このプロジェクトにはid tokenaccess token の両方を使っていきます。スコープにopenid、profile、およびemailが入ってきます。

service.jsを設定する

service.jsファイル内の機能の至るところで異なる変数を使っていきます。それらを設定して宣言しましょう。idTokenまたはidTokenPayloadのようなものをヌルに設定して、必要なときにそれら値を与えられるようにします。

// src/auth/service.js

// インポートします...

class Auth {
  // auth0 定義...

  loginCallback = () => {};
  logoutCallback = () => {};

  userProfile = null;
  authFlag = "isLoggedIn";
  authStatus = this.isAuthenticated // まもなく isAuthenticated が作成されます
    ? "init_with_auth_flag"
    : "init_no_auth_flag";
  idToken = null;
  idTokenPayload = null;
  accessToken;

}

localLoginおよびlocalLogout

ユーザーがログインすると、数個の異なる機能を有効にします。login関数(まもなく説明します)は非常に重要ですが、その関数は localLogin を呼び出します。この関数には、多数の値を上記で設定したばかりの変数に保存していきます。userProfileidTokenPayloadの認証結果が出てい、次のような情報を持ちます。

  • Eメール
  • 姓名
  • 写真(提供される場合)

accessTokenを保存して後ほど、ヘッダーにそれを保存できるようにします。

Auth0では、ユーザー情報をlocalStorageに保存しないようにお勧めしていますが、authFlaglocalStorageに保存します。それを真に設定して、この特定セッション状態を保存できます。

localLogout関数もここで構築していきます。これは消去したい情報を消去してnullに設定します。

// src/auth/service.js

class Auth {
  // auth0 定義...  
  // その他のメソッド...

  localLogin(authResult) {
    localStorage.setItem(this.authFlag, true);
    this.idToken = authResult.idToken;
    this.userProfile = authResult.idTokenPayload;
    this.accessToken = authResult.accessToken;
    this.loginCallback({ loggedIn: true });
  }

  localLogout() {
    localStorage.removeItem(this.authFlag);
    this.userProfile = null;
    this.logoutCallback({ loggedIn: false });
  }

}

accessTokenにアクセスする

次に、accessTokenの値を保留する関数を作り後ほどそれを使えるようにします。

// src/auth/service.js
class Auth {
  // auth0 definition...
  // other methods...

  getAccessToken() {
    return this.accessToken;
  }

}

ログインする

ユーザーがLog Inボタンまたはどちらか選択した方法をクリックすると、login関数が送信してスムーズな体験を与えます。login関数は前に設定したauth0変数とpopup関数を使います。

この行をlogin関数で実行すると次のようになります。

this.auth0.popup.authorize()

popupの素晴らしい関数を有効にすることができます。

認証されたかどうかを確認します。

そのユーザー情報が通過したら、この関数はこれから使うaccessTokenを付与します。通過しなければ、エラーが発生し、localLogout()がそこから対処します。

localStorage authFlag"true"に設定するためにisAuthenticated()に追加していきます。

// src/auth/service.js

class Auth {
  // auth0 定義...  
  // その他のメソッド...

  login() {
    this.auth0.popup.authorize({}, (err, authResult) => {
      console.log(err, authResult);
      if (err) this.localLogout();
      else {
        this.localLogin(authResult);
        this.accessToken = authResult.accessToken;
      }
    });
  }

  isAuthenticated() {
    return localStorage.getItem(this.authFlag) === "true";
  }

}

ログアウトする

ユーザーがLog Outをクリックすると、認証に保存・使用したばかりの情報を消去します。localLogoutを呼び出してそれを実行します。

configファイルに設定したlogoutUrlにそれらを取り込みます。

// src/auth/service.js

class Auth {
  // auth0 定義...  
  // その他のメソッド...

  logout() {
    this.localLogout();
    this.auth0.logout({
      returnTo: config.logoutUrl,
      clientID: config.clientId
    });
  }

}

service.jsをエクスポートする

この情報をアプリケーションで自由に使えるようにしたいので、authnew Auth()に設定します。これをすることで、これら関数を親および子コンポーネントで使えるようになります。

このモジュールでホストされるAuth()のインスタンスを作り、それをインポートするすべてをアプリケーションで使えるようにします。

それを宣言したら、このファイルをexportします。これで完成です!

// src/auth/service.js

// これらはファイルの最後の2行です

const auth = new Auth();

export default auth;

フロントエンド コンポーネント

アプリケーションに3つのコンポーネントを使っていきます。ひとつは親コンポーネント、2つは子コンポーネントです。

  • App.js
  • TvShows.js
  • Movies.js

App.jsコンポーネントはすでにあり、作られています。create-react-appを実行すると、初期ビューをそこに構築します。そのファイルに行ってそれを消去できますが、これから多数を変更していきます。

App.js

ファイルの上部に、必要なものをインポートしましょう。

// src/App.js

import React, { Component } from "react";
import TvShows from "./components/TvShows.js";
import Movies from "./components/Movies.js";
import auth from "./auth/service";

これらインポートの2つはまだ作っておらず(これらは子コンポーネントになります)、auth/service.jsファイルが入ってきます。

AppコンポーネントはLog InおよびLog Outボタンを処理し、どのコンポーネントが表示されるかを決めます。

状態を次のように設定します。

this.state = { loggedIn: false };

// フルコードは以下の通りです

それから、一部の関数をバインドします。

auth.loginCallback = this.loggedIn.bind(this);
auth.logoutCallback = this.loggedOut.bind(this);

// フルコードは以下の通りです

これら2つの関数は状態を切り替えます。loggedIn()が呼び出されたら、loggedIn状態がtrueに変わるようにします。loggedOut()にはその反対が発生します。そのコードを間もなく見ていきます。

ES6三項の機能を使ってLog InおよびLog Outボタンと、どのコンポーネントをページに表示するかを切り替えます。

ES6 三項についての詳細情報は このページをご覧ください。

そのロジックが設定されたら、コンポーネントは次のようになります。

// src/App.js

class App extends Component {
  constructor(props) {
    super(props);

    auth.loginCallback = this.loggedIn.bind(this);
    auth.logoutCallback = this.loggedOut.bind(this);

    this.state = { loggedIn: false };
  }

  loggedIn() {
    this.setState({ loggedIn: true });
  }

  loggedOut() {
    this.setState({ loggedIn: false });
  }

  render() {
    return (
      <div>
        {this.state.loggedIn ? <Movies /> : <TvShows />}
        {this.state.loggedIn ? (
          <button onClick={() => auth.logout()} className="log-in">
            Log Out
          </button>
        ) : (
          <button onClick={() => auth.login()} className="log-in">
            Log In
          </button>
        )}
      </div>
    );
  }
}

export default App;

テレビ番組および映画のコンポーネント

TvShows.jsおよびMovies.jsの2つのコンポーネントを使っていきます。これら両者では、React Hooksを使っていきます。コンポーネント自体は見慣れた感じがしますが、その関数はまったく別に実行します。

まず、TvShows.jsコンポーネントを作ります。srcフォルダーに componentsという名前の別のフォルダーを作ります。そのフォルダー内に、次の3つのファイルを作ります。

  • TvShows.js
  • Movies.js
  • styles.css

React Hooksを使う

これらファイルではReact Hooks を使います。useStateおよびuseEffectのようなReact機能をインポートしていきます。

useStateはsetStateのようなものですが、機能コンポーネントで使用でき、クラスを書く必要はありません!またuseEffectはcomponentWillMountのようなライフサイクル メソッドの代わりに使用できます。

テレビ番組コンポーネント

ファイルの上部components/TvShows.jsに必要なものをインポートしていきます。

// src/components/TvShows.js

import React, { useState, useEffect } from "react";
import "./styles.css";

それから次のコンポーネントを開始します。

// src/components/TvShows.js

// Imports here

function TvShows() {

}

stateをコンポーネントに設定するには、少し異なった方法で行っていきます。

// src/component/TvShows.js

// ここをインポートします

function TvShows() {
    const [shows, setShows] = useState([]);
}
  1. 変数showsthis.stateと同様です。
  2. ふたつめは、setShowsthis.setStateと同様です。
  3. みっつめは、useState([])は初期状態値でになります。

これらはご存知ですね!

次に、componentDidMountの代わりにuseEffectを使用していきます。fetchを使用してAPIのデータを呼び出し、応答を受け取りましょう。

// src/component/TvShows.js

// ここをインポートします

function TvShows() {
    const [shows, setShows] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/tvShows")
      .then(response => response.json())
      .then(data => {
        setShows(data);
      });
  }, []);
}

shows.nameおよびshows.airDateを表示しますが、選択するリストは次のようにたくさんあります。

  1. id
  2. name
  3. airDate
  4. lastShowDate
  5. aCastMember
  6. totalSeasons
  7. totalEpisodes
  8. interestingFact
// src/components/TvShows.js

import React, { useState, useEffect } from "react";
import "./styles.css";

function TvShows() {
  const [shows, setShows] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/tvShows")
      .then(response => response.json())
      .then(data => {
        setShows(data);
      });
  }, []);

  return (
    <div>
      <h1>TV SHOWS</h1>
      {shows.map(shows => (
        <div className="card" key={shows.id}>
          <h3>{shows.name}</h3>
          <h5>{shows.airDate}</h5>
        </div>
      ))}
    </div>
  );
}

export default TvShows;

この情報はユーザーが認証されていようがなかろうが許可されていますが、この情報は「保護」されていません。

映画コンポーネント

このコンポーネントはTvShows.jsコンポーネントに非常によく似てくるでしょう。主な違いは次のとおりです。

  • accessTokenを保存する
  • /auth/serviceファイルをインポートする

/auth/service.jsファルからgetAccessToken()を使用することで、accessTokenをヘッダーに保存し、API映画ルートを呼び出していきます。

この時点ではmovies.nameおよびmovies.airDateを閲覧しますが、選択するリストはたくさんあります。

ユーザーがログインしたら、このビューに移動し、そのビューにはTvShowsコンポーネントもあります。そうすることで、認証が終わったらユーザーは両方のコンポーネントを同時に見ることができます。

// src/component/Movies.js

import React, { useState, useEffect } from "react";
import "./styles.css";
import TvShows from "./TvShows";
import auth from "../auth/service";

function Movies() {
  const [movies, setMovies] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3005/api/data/movies", {
      headers: {
        Authorization: `Bearer ${auth.getAccessToken()}`
      }
    })
      .then(response => response.json())
      .then(data => {
        setMovies(data);
      });
  }, []);

  return (
    <div>
      <h1>MOVIES</h1>
      {movies.map(movies => (
        <div key={movies.id} className="card">
          <h3>{movies.name}</h3>
          <h5>{movies.airDate}</h5>
        </div>
      ))}
      <TvShows />
    </div>
  );
}

export default Movies;

スタイル

ユーザー インターフェイスを少し整頓しましょう!

/* src/components/styles.css */

.App {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

h3 {
  color: rgb(33, 124, 94);
}

.card {
  border: 2px solid black;
  width: 20%;
  background-color: peachpuff;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  margin: 20px;
  font-size: 12px;
}

.log-in {
  width: 15%;
  color: peachpuff;
  background-color: rgb(33, 124, 94);
  font-size: 25px;
  margin: 20px;
}

h1 {
  color: rgb(33, 124, 94);
  margin: 20px;
}

完成アプリケーション

npm startを使ってブラウザーでlocalhost:3000を実行するとき、以下が表示されます。

React Popup App unprotected view of Tv Shows

ユーザーが「ログイン」をクリックすると、現在のビューから離れずにポップアップ ウィンドウが表示されます。

Auth0 Popup login modal window view

ユーザーはその資格情報でログインするとポップアップは非表示になり、Moviesコンポーネントが表示されます。ユーザーは映画やテレビ番組のリストが見えるようになります。

Authenticated view of Tv Shows and Movies

Log InボタンはLog Outボタンに変わり、そのページを「検査」するのであれば、「ネットワーク」タブに移動し、「映画」をクリックして、「ヘッダー」部分で下にスクロールするとaccessTokenが保存されているのが分かります。

Logout button

Authorization: Bearer "token"

ユーザーが「ログアウト」をクリックすると、「テレビ番組」の非認証ビューに移動し、そのセッションは終了します。

Auth0 について

Auth0 は IDaaS (Identity-as-a-Service)におけるグローバルリーダーで、Web やモバイル、モノのインターネット (IoT)、内部アプリケーションで必要とされるユニバーサル ID プラットフォームで何千もの企業顧客に提供しています。ひと月25億回以上のログインをシームレスに認証・保護するその拡張プラットフォームはデベロッパーに愛され、グローバル企業に信頼されています。米国本社はワシントン州ベルビュー市に位置し、ブエノスアイレス、ロンドン、東京、シドニーにもオフィスを構え、70か国以上のお客様をサポートしています。

詳細については、https://auth0.com/jp/ をご覧いただくか、@auth0_jp on Twitter をフォローしてください。

まとめ

ログインするポップアップ方法はユーザーを現在のスクリーンからリダイレクトしないので便利です。この不都合な点はたくさんのユーザーがポップアップをブロックしているので、認証には最善の方法でないかもしれません。

このプロジェクトでは、ポップアップ方法を実装して新規React Hooksを使用します。プロジェクトによってはポップアップを使うのがいい場合もあります。