今回、4階部分(React/JSX)の後半である。前回はこちらから。今回、Authenticatorの開発を行う。

React(JSX)を使って、ログイン後のマイページトップ画面とユーザー情報変更画面を作っていく。

4階:Reactで画面を作る<Authenticator編>
Authenticatorで実現したいこと
逆引き的に考え、以下の3つを実現したい。Authenticatorのリファレンスを参考にしつつ進める。
- 全ページログイン必須とし、実ページと処理を分離する
- ニックネームなど他の属性を追加する
- 追加した属性を変更できるようにする
■Authenticator | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator
具体的には以下のような画面遷移の構造を想定する。

1.全ページログイン必須とし、実ページと処理を分離する
最初に悩んだ点が、別ページ(別コンポーネント)に分離した際にログイン情報をどのようにして引き渡すか、という点。「props」というパラメーターを用いて引き渡すことは可能だが、属性が増えれば増えるほど処理が煩雑になってしまう。
今回は、useAuthenticator という機能を使う。この機能により親のコンポーネントから子のコンポーネントに値を引き渡すことができる(後でわかるが、ReactのContextと同等の機能である)。
LoginPage → MyPage
最初に、LoginPage → MyPage の実装を行う。

以下のリファレンスを確認しながら進める。
■Headless | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator/headless
変更前の親コンポーネントであるLoginPage.jsは以下の通り。前回作成したソースコードは以下の通り。変更したい箇所を削除する。
■/src/pages/LoginPage.js
import { Authenticator } from '@aws-amplify/ui-react';
import { Link } from "react-router-dom"; ←削除
const LoginPage = () => {
return (
<Authenticator>
{({ signOut, user }) => (
↓削除
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
<p><Link to="/blank">ブランクページへ</Link></p>
</main>
↑削除
)}
</Authenticator>
);
}
export default LoginPage;
親コンポーネントにおいて、<Authenticator.Provider> 内で子コンポーネント(MyPage)を呼び出す。変更後のLoginPage.jsは以下の通り。
■/src/pages/LoginPage.js
import { Authenticator } from '@aws-amplify/ui-react';
import MyPage from "./MyPage"; ←追加
const LoginPage = () => {
return (
<Authenticator>
{({ signOut, user }) => (
↓追加
<Authenticator.Provider>
<MyPage />
</Authenticator.Provider>
↑追加
)}
</Authenticator>
);
}
export default LoginPage;
新しく作る子コンポーネントのMyPage.jsは以下とする。
■/src/pages/MyPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Link } from "react-router-dom";
const MyPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
return (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
<p><Link to="/user_edit">ユーザー情報変更</Link></p>
</main>
);
}
export default MyPage;
子コンポーネント内で、useAuthenticator フックでcontext.userを指定することで、ユーザー情報(user)を引き継ぐことができる。
2.ニックネームなど他の属性を追加する
追加できる全ての属性は以下に記載されている。
■Configuration | Amplify UI for React
https://ui.docs.amplify.aws/react/connected-components/authenticator/configuration#sign-up-attributes

実際に組み込んで試してみる。
■/src/pages/LoginPage.js
import { Authenticator } from '@aws-amplify/ui-react';
import MyPage from "./MyPage";
const LoginPage = () => {
return (
<Authenticator signUpAttributes={[
'address', // Not displayed
'birthdate',
'email',
'family_name',
'gender', // Not displayed
'given_name',
'locale', // Not displayed
'middle_name', // Not used in Japan
'name',
'nickname',
'phone_number',
'picture', // Not displayed
'preferred_username', // Not used in Japan
'profile',
'updated_at', // Not displayed
'website',
'zoneinfo', // Not displayed
]}>
{({ signOut, user }) => (
<Authenticator.Provider>
<MyPage />
</Authenticator.Provider>
)}
</Authenticator>
);
}
export default LoginPage;

先ほどのリファレンス「Sign Up Attributes」を確認すると、自動でレンダリングされない項目が存在するとある。
The Authenticator automatically renders most Cognito User Pools attributes, with the exception of
Configuration | Amplify UI for Reactaddress
,gender
,locale
,picture
,updated_at
, andzoneinfo
. Because these are often app-specific, they can be customized via Sign Up fields.
以下に詳しい解説があるが、今回はスキップする。
■User pool attributes – Amazon Cognito
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
■Final: OpenID Connect Core 1.0 incorporating errata set 1
https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
実際にアカウント作成をしようとすると、なぜかProfile欄で「URLを入力してください。」と怒られる。

Cognitoを確認すると、Websiteは登録されていない。

原因はよくわからないがProfileとWebsiteは使わない方が無難かもしれない。
また、日本では馴染みのない「ミドルネーム」などを除くと、デフォルトで使える属性は以下になる。
- birthday
- family_name
- given_name
- name
- nickname
- phone_number
修正後のソースは以下の通り。
■/src/pages/LoginPage.js
import { Authenticator } from '@aws-amplify/ui-react';
import MyPage from "./MyPage";
const LoginPage = () => {
return (
<Authenticator signUpAttributes={[
'birthdate',
'email',
'family_name',
'given_name',
'name',
'nickname',
'phone_number',
]}>
{({ signOut, user }) => (
<Authenticator.Provider>
<MyPage />
</Authenticator.Provider>
)}
</Authenticator>
);
}
export default LoginPage;
MyPage.jsを修正して属性の値を確かめる。
user.attributes.birthdate のようにattributesで修飾するのがポイントである。ちなみに、usernameは自動でユニークに振られるIDのため変更はできない。
■/src/pages/MyPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { Link } from "react-router-dom";
const MyPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
return (
<main>
<h1>Hello {user.username}</h1>
<li>birthdate:{user.attributes.birthdate}</li>
<li>email:{user.attributes.email}</li>
<li>family_name:{user.attributes.family_name}</li>
<li>given_name:{user.attributes.given_name}</li>
<li>name:{user.attributes.name}</li>
<li>nickname:{user.attributes.nickname}</li>
<li>phone_number:{user.phone_number}</li>
<button onClick={signOut}>Sign out</button>
<p><Link to="/user_edit">ユーザー情報変更</Link></p>
</main>
);
}
export default MyPage;
以下のように表示される。

phone_numberは、「未認証」なので表示されていない。認証する手順もあるとは思うが脇道に逸れるため、今回はphone_numberを属性変更の対象外とする。
3.追加した属性を変更できるようにする
UserEdit → UserEditPage
次に、UserEdit → UserEditPage の実装を行う。

■/src/pages/UserEdit.js
import { Authenticator } from '@aws-amplify/ui-react';
import UserEditPage from "./UserEditPage";
const UserEdit = () => {
return (
<Authenticator signUpAttributes={[
'birthdate',
'email',
'family_name',
'given_name',
'name',
'nickname',
'phone_number',
]}>
{({ signOut, user }) => (
<Authenticator.Provider>
<UserEditPage />
</Authenticator.Provider>
)}
</Authenticator>
);
}
export default UserEdit;
子コンポーネントのUserEditPage.js、まずは初期値を表示する実装を行う。
■/src/pages/UserEditPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
const UserEditPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
return (
<main>
<li>birthdate: <input defaultValue={user.attributes.birthdate} /></li>
<li>email: <input defaultValue={user.attributes.email} /></li>
<li>family_name: <input defaultValue={user.attributes.family_name} /></li>
<li>given_name: <input defaultValue={user.attributes.given_name} /></li>
<li>name: <input defaultValue={user.attributes.name} /></li>
<li>nickname: <input defaultValue={user.attributes.nickname} /></li>
<p><button>ユーザー情報を変更する</button></p>
<p><button onClick={signOut}>ログアウトする</button></p>
</main>
);
}
export default UserEditPage;
属性(ニックネーム)のステートを保持
例としてニックネームを変更する。ReactのフックであるuseState() を使い、ユーザーが入力した値を保持する。また、onChangeイベント発生時にニックネームを最新化する。
このあたりの話は調べれば出てくるが、React話のうんちくから入ると非常に入りにくいと(個人的には)思う。まずは動かしてみた後に確認すると理解が早い。
ちなみに、動作確認用に変更したニックネームをコンソールに表示するようにしている。
■/src/pages/UserEditPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { useState } from "react"; ←追加
const UserEditPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
const [nickname, setNickname] = useState(user.attributes.nickname); ←追加
console.log(nickname); ←追加
return (
<main>
<li>birthdate: <input defaultValue={user.attributes.birthdate} /></li>
<li>email: <input defaultValue={user.attributes.email} /></li>
<li>family_name: <input defaultValue={user.attributes.family_name} /></li>
<li>given_name: <input defaultValue={user.attributes.given_name} /></li>
<li>name: <input defaultValue={user.attributes.name} /></li>
<li>nickname: <input defaultValue={nickname} onChange={e => setNickname(e.target.value)} /></li> ←変更
<p><button>ユーザー情報を変更する</button></p>
<p><button onClick={signOut}>ログアウトする</button></p>
</main>
);
}
export default UserEditPage;
App.js にRouteを追加する。
■/src/App.js
import { Amplify } from 'aws-amplify';
import '@aws-amplify/ui-react/styles.css';
import { Route, Routes, BrowserRouter } from "react-router-dom";
import BlankPage from "./pages/BlankPage"; ←削除
import LoginPage from "./pages/LoginPage";
import UserEdit from "./pages/UserEdit"; ←追加
import awsExports from './aws-exports';
Amplify.configure(awsExports);
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/blank" element={<BlankPage />} /> ←削除
<Route path="/user_edit" element={<UserEdit />} /> ←追加
</Routes>
</BrowserRouter>
);
}
export default App;
以下のようにコンソールに表示される。

ユーザー属性(ニックネーム)の更新
ユーザー情報を更新するメソッドを追加し、ボタンクリックでそのメソッドを呼び出す。以下を参考に実装する。
■Authentication – Managing user attributes – JavaScript – AWS Amplify Docs
https://docs.amplify.aws/guides/authentication/managing-user-attributes/q/platform/js/#writing-and-updating-standard-attributes
■/src/pages/UserEditPage.js
import { useAuthenticator } from '@aws-amplify/ui-react';
import { useState } from "react";
import { Auth } from 'aws-amplify'; ←追加
const UserEditPage = () => {
const { user, signOut } = useAuthenticator((context) => [context.user]);
const [nickname, setNickname] = useState(user.attributes.nickname);
console.log(nickname);
↓追加
async function updateUserInfo() {
let result = null;
try {
const user = await Auth.currentAuthenticatedUser();
result = await Auth.updateUserAttributes(user, {
nickname: nickname
});
} catch(error) {
console.log('error update: ', error);
}
if (result === 'SUCCESS') {
alert("ユーザー情報を更新しました!");
}
else {
alert("ユーザー情報の更新に失敗しました・・・");
}
return result;
}
↑追加
return (
<main>
<li>birthdate: <input defaultValue={user.attributes.birthdate} /></li>
<li>email: <input defaultValue={user.attributes.email} /></li>
<li>family_name: <input defaultValue={user.attributes.family_name} /></li>
<li>given_name: <input defaultValue={user.attributes.given_name} /></li>
<li>name: <input defaultValue={user.attributes.name} /></li>
<li>nickname: <input defaultValue={nickname} onChange={e => setNickname(e.target.value)} /></li>
<p><button onClick={() => updateUserInfo()}>ユーザー情報を変更する</button></p> ←変更
<p><button onClick={signOut}>ログアウトする</button></p>
</main>
);
}
export default UserEditPage;
ニックネームを変更して更新することができた。

ニックネーム以外も同様に実装することで、ユーザー情報の更新が可能になる。
次回は、見た目を整えるべくCSSを追記する予定である。