AWS amplyfy-jsで Authenticator を利用するときの勘所 

## はじめに
　We Are JavaScripters AC 初カキコ...ども...
　本記事は We Are JavaScripters!【執筆初心者歓迎】 Advent Calendar 2018 (https://adventar.org/calendars/2972) の 12/5 担当分の記事です。
　前回は `brn` さんでした。

　We Are JavaScripters 関連は前から気になっていので書きました。
　ツッコミどころあればコメントください〜。

## 本記事の目的
- AWS AmplifyのAuth周りを使ってつまずきそうなポイントをメモ
  - 今後使う予定のある方のつまずきダメージを減らす効果をねらいつつ〜

## 本記事の前に読んでおくと良いポイント
- AWS amplify
  - https://aws-amplify.github.io
- AWSの次世代JavaScriptライブラリ「AWS Amplify」の概要とReactアプリに導入する手順component
  - https://dev.classmethod.jp/server-side/serverless/aws-amplify-getting-started/

## つまずきポイント
- そもそもAmplify-jsをどう使っていいか分からない問題
- 認証フローに自分の作ったcomponentを追加してみる
- 自分で作ったcomponentをimportしてみたが、自分のcomponentに遷移しない問題
- component間遷移時のデータをどう渡せば良いか分からない問題
- Amplify-jsのデフォルトcomponentにオリジナルのデザインをつける

### そもそもAWS Amplify-jsそのものがよく分からない問題
- Authで触ってるとUIを一撃で作ってくれる便利ライブラリと認識しがち

```jsx
import { withAuthenticator } from 'aws-amplify-react'; // or 'aws-amplify-react-native';
...
export default withAuthenticator(App);
```

- まずはAPI集ということで理解した方がいい
  - Auth系API一覧はこちら
  - https://aws-amplify.github.io/amplify-js/api/classes/authclass.html

- データ取り扱いがちょっと独特
  - `authState`
      - 認証フローでどういう状態かを示すデータ
  - `authData`
      - 認証フローの中でやり取りするデータ

- AWS sdk for javascriptもあるが、React, Angular経由で使うならAWS Amplifyを使う `昨今の流れ`
  - https://github.com/aws/aws-sdk-js#with-react
  - https://github.com/aws/aws-sdk-js#with-angular

```
With React

To create React applications with AWS SDK, you can use AWS Amplify Library which provides React components and CLI support to work with AWS services.
With Angular

Due to the SDK's reliance on node.js typings, you may encounter compilation issues when using the typings provided by the SDK in an Angular project created using the Angular CLI.

To resolve these issues, either add "types": ["node"] to the project's tsconfig.app.json file, or remove the "types" field entirely.

AWS Amplify Library provides Angular components and CLI support to work with AWS services.
```
- promiseについてはなんとなく知っておくと元ソースで仕組みを把握しやすい
  - 結果としてuserPoolのAPIの話なんだなとか追いかけ先が分かる

```jsx
return new Promise((resolve, reject) => {
  this.userPool.signUp(username, password, attributes, validationData, function(err, data) {
    if (err) {
      dispatchAuthEvent('signUp_failure', err);
      reject(err);
    } else {
      dispatchAuthEvent('signUp', data);
      resolve(data);
    }
  });
});

```
  - https://github.com/aws-amplify/amplify-js/blob/master/packages/auth/src/Auth.ts#L251


### 自分の component を追加するけど、別のcomponentに遷移しない問題
- 例えばオリジナルのサインアップViewを作ったとして

```jsx
import { Authenticator, SignUp, SignIn } from 'aws-amplify-react';

<Authenticator hideDefault={true}>
  <SignIn />
  <MyCustomSignUp override={SignUp}/> {/* to tell the Authenticator the SignUp component is not hidden but overrided */}
</Authenticator>


// この程度ならfunctionalで良い気がするけど
class MyCustomSignUp extends SignUp {
  constructor() {
    super();
  }

  gotoSignIn = () => {
    // これで飛ぶ
    this.props.onStateChange('signIn'); 
  }

  render() {
    return (
      <div>
        {/* signUp状態だとrender */}
        { this.props.authState === 'signUp' && 
        <div>
          My Custom SignUp Component
          <button onClick={this.gotoSignIn}>Goto SignIn</button>
        </div>
        }
      </div>
    );
  }
}

```

- this.props.authState('移動先'); で移動する
- 逆向きで自分の component からAuthenticator管理外のとこから帰ってくるときもpropsから引っ張って使うと良い
- 実はdocsに書いてあります (ただし一番下)

### 認証フローに自分の作ったcomponentを追加してみる
- authStateを被らせないように注意

```jsx
// この程度ならfunctionalで良い気がするけど
class MyAuthFlowView extends SignUp {
  constructor() {
    super();
    // これで 'myAuthFlowView' を定義できた (かぶらないように注意！)
    this._validAuthStates = ['myAuthFlowView'];
  }
  render () {
    // 例えば個人情報同意viewとか
  }
}
:

function otherView {
  render() {
    return (
      <div>
        <div>
          My Custom SignUp Component
          <button onClick={()=>this.props.onStateChange('myAuthFlowView')}>Goto SignIn</button>
        </div>
        }
      </div>
    );
  }
}

```

### component 間遷移時のデータの引っ越し
- 前述のauthDataをauthStateの後ろにくっつけて渡せる

```jsx
import { Authenticator, SignUp, SignIn } from 'aws-amplify-react';

<Authenticator hideDefault={true}>
  <SignIn />
  <MyCustomSignUp override={SignUp}/> {/* to tell the Authenticator the SignUp component is not hidden but overrided */}
</Authenticator>


// この程度ならfunctionalで良い気がするけど
class MyCustomSignUp extends SignUp {
  constructor() {
    super();
  }

  gotoSignIn = () => {
    const xmas = {
      kanojo: false,
      schedule: false,
      twitter: true,
      colaLitre: 10,
      chicken: '2kg',
      takeuchiMariya: 'every year'
    };
    // これで渡せる
    this.props.onStateChange('signIn', xmas);
  }

  render() {
    return (
      <div>
        {/* signUp状態だとrender */}
        { this.props.authState === 'signUp' && 
        <div>
          My Custom SignUp Component
          <button onClick={this.gotoSignIn}>Goto SignIn</button>
        </div>
        }
      </div>
    );
  }
}

```

- 受け取るときは `authData` というプロパティの子として渡されるので、へたなことすると `authData.authData.xmas` とかになっちゃうので注意
  - `this.props.onStateChange('signIn', this.props.authData);` とかしたとき

### Amplify-jsのデフォルトcomponentにオリジナルのデザインをつける
- UIThemeを上書きしようね的なことが書いてある

```js

import { AmplifyTheme } from 'aws-amplify';

const MySectionHeader = Object.assign({}, AmplifyTheme.sectionHeader, { background: 'orange' });
const MyTheme = Object.assign({}, AmplifyTheme, { sectionHeader: MySectionHeader });

<Authenticator theme={MyTheme} />
```

- https://aws-amplify.github.io/docs/js/authentication#customize-ui-theme

- とはいえ自分のcomponentもあるし
  - 例えばログイン後ページは自作して、ログイン後からのページ遷移はreact-routerしちゃったり
  - router管理の component はデザインさんと分業してたりするパターン

- 一番手っ取り早いのはstyle.cssにまとめた方が良いです
  - className はどうせ文字列なのでどうとでもなる
  - alt-css通すならなおのこと
- 逆にthemeとcssで散らせると管理が厄介になります
  - ましてcomponent内にjson定義してstyle={myStyle}とかで渡すとつらい
  - theme, style.css, component内 styleの3つ建てとかになっちゃう

## さいごに
以上、Amplify-jsのつまずきポイントをメモりました。
次回は `kei4eva4` さんです。では〜。