初めに
React+Redux+typescript+firebaseで認証機能作るのが辛い②の続き
そんなに苦労はしなかったけど一応書いておく。やったのはrouter使ってログインしていたらHomeにいって、それ以外ならLogin画面に遷移する。
作る機能と、この記事の立ち位置
作る機能
・firebaseで認証機能の実現
・React,Redux,typescriptを利用した実装
・セッション切れたらログイン画面へリダイレクトなど基本的認証の機能を実現
記事の立ち位置
・Reactとfirebaseで認証機能作る //①
・ReactとReduxとfirebaseで認証機能作る //②
・Routeで認証するかホーム画面へ遷移するかを分ける //③ ←ココ
App.tsx
基本はReact-Router v4を使って認証状態でリダイレクトする処理を参考にしました。tsに少し直した感じです。コードは以下。
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内だけ説明します。
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が表示されるのもどうかとは思いますが)
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コンポーネントはログイン画面を作っています。すごい手抜き。
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のものを使い回しています。
終わりに
1から作ったら結構苦労した気がしましたが実装してみるとだいぶ少ないなと思いました。