React+Redux+typescript+firebaseで認証機能作るのが辛い③

# 初めに
[React+Redux+typescript+firebaseで認証機能作るのが辛い②](https://qiita.com/teshima/items/2191a9751a4ab16e7ec2)の続き

そんなに苦労はしなかったけど一応書いておく。やったのはrouter使ってログインしていたらHomeにいって、それ以外ならLogin画面に遷移する。

# 作る機能と、この記事の立ち位置
### 作る機能
　・firebaseで認証機能の実現
　・React,Redux,typescriptを利用した実装
　・セッション切れたらログイン画面へリダイレクトなど基本的認証の機能を実現

### 記事の立ち位置
#### ・[Reactとfirebaseで認証機能作る](https://qiita.com/teshima/items/8ccf41cf7e297a6489e0) //①
#### ・[ReactとReduxとfirebaseで認証機能作る](https://qiita.com/teshima/items/2191a9751a4ab16e7ec2) //②
#### ・Routeで認証するかホーム画面へ遷移するかを分ける //③ ←ココ


# App.tsx
基本は[React-Router v4を使って認証状態でリダイレクトする処理](https://qiita.com/soutaito/items/691ac9dabe765e98d9f9)を参考にしました。tsに少し直した感じです。コードは以下。

```App.tsx
import * as React from 'react'
import './App.css'
import AuthContainer from './containers/AuthContainer';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import Login from './modules/Login';
import HomeContainer from './containers/HomeContainer';

class App extends React.Component<any> {

  public render() {
    return (
      <BrowserRouter>
        <Switch>
          <Route exact={true} path='/login' component={Login} />
          <AuthContainer>
            <Switch>
              <Route exact={true} path="/" component={HomeContainer} />
            </Switch>
          </AuthContainer>
        </Switch>
      </BrowserRouter>
    )
  }
}

export default App
```

react-router-domを事前にnpm installしておく。(v4を使用しています)
`<BrowserRouter>`で囲った中でRoutingされる。`<Switch>`以下の`<Route>`で、各pathにアクセスしたときに時のcomponentを分けている。で、`Route`に並行して`<AuthContainer>`を配置している。こうしておくことで、画面遷移が、"/login"の時以外`<AuthContainer>`を経由するようになる。つまり、login画面はauth関係なく表示されるが、それ以外は認証チェックが走る。Homeコンポーネントは、ログイン後に表示する画面を作っているだけで、ぶっちゃけなんでもいいので今回は割愛。

# Auth関連
Authについて説明します。前回の記事とかなりかぶるため、変更したComponent内だけ説明します。

```Auth.tsx

export class Auth extends React.Component<Props> {

    private isLoading: boolean = true

    public componentDidMount() {
        this.props.refLogin()
        this.isLoading = false
    }

    public render() {
        return (
            this.props.userInfo.uid ? (
                <Route children={this.props.children} />
            ) : (
                    this.isLoading ? (
                        <div>Loading</div>
                    ) :
                        <Redirect to={'/login'} />
                )
        )
    }
}
```
`private isLoading: boolean = true`は、auth認証で非同期リクエストを投げるため、レスポンスを待つ間画面を表示させないように用意しています。これがないと、認証ずみでも画面更新のたびにlogin画面がちらついてしまうのがちょっとなーと思い追加しました。（loadingが表示されるのもどうかとは思いますが）

```ts

this.props.userInfo.uid ? (
                <Route children={this.props.children} />
            ) : (
                    this.isLoading ? (
                        <div>Loading</div>
                    ) :
                        <Redirect to={'/login'} />
                )
```
ここで、`userInfo.uid`の有無を確認しています。uidはfirebaseから取ってきています（前回参照）。uidがあるときには、`<Route children={this.props.children} />`を表示します。childrenはApp.tsxで定義しているAppContainer以下のconponentに該当します。
uidが無い場合は、`this.isLoading`を参照して、Loadingを表示するか、`<Redirect to={'/login'} />`で/loginにリダイレクトします。これで、auth認証リクエストが帰ってきてなおかつuidがなければlogin画面を表示するようになります。

# Login
Loginコンポーネントはログイン画面を作っています。すごい手抜き。

```Login.tsx
import { Props, authActions } from './Auth';
import * as React from 'react';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import { ReduxState } from 'src/store';
import { Dispatch, Action } from 'redux';
import firebase from '../firebase'
import { setUserInfo } from 'src/containers/AuthContainer';

class Login extends React.Component<Props> {

    public render() {
        return (
            this.props.userInfo.uid ? (
                <Redirect to={'/'} />
            ) : (
                    <button onClick={this.props.login}>Google Login</button>
                )
        );
    }
}

function mapDispatchToProps(dispatch: Dispatch<Action<string>>) {
    return {
        login: () => {
            const provider = new firebase.auth.GoogleAuthProvider()
            firebase.auth().signInWithPopup(provider)
                .then(response => {
                    dispatch(authActions.login(setUserInfo(response.user)))
                })
        }
    };
}

function mapStateToProps(state: ReduxState) {
    return Object.assign({}, { userInfo: state.userInfo });
}

export default connect(
    mapStateToProps,
    mapDispatchToProps
)(Login);
```
特に説明することはないです。login時のstate変更はauthと同じため、actionはauthのものを使い回しています。

# 終わりに
１から作ったら結構苦労した気がしましたが実装してみるとだいぶ少ないなと思いました。