React + TypeScript + Firebase でユーザ登録_その1
はじめに
だいきちです。
最近、React + TypeScript
でアプリを作成しています。
その中でユーザ認証周りをFirebaseに任せたいなー
と思いまして、色々調べながら環境とサンプルを作成しました。
情報が古かったり、手順が不十分でわかりにくかったりしたので
GitHubでのサンプル公開と、手順解説をしてみようと思いました。
長いのでパートごとに分けています。
リポジトリ
サンプルのリポジトリはこちら。
github.com
環境
$ npx --version 6.2.0 # npxのバージョン $ npx create-react-app --version 2.1.3 # create-react-appのバージョン
ここでcreate-react-app
がインストールされていない場合は、インストールを行いましょう。
$ npm install -g create-react-app
以後、create-react-app
は文中ではCRA
として扱います。
ここまでで準備完了です。
CRA + TypeScript
まずはCRA+TypeScriptで環境構築。
$ npx create-react-app [プロジェクト名] --typescript
これでCRA + TypeScriptの環境ができました。
consoleにnpm start
と打ちこんで、初期画面が表示されるか確認しましょう。
react-router-domとfirebaseの導入
次に、routingを有効にする為のreact-router-dom
。
そして、firebase
の導入を行います。
本来はreact-routerは入れなくても良いですが、今後
サンプルを使い回す際に有効かと思い、採用しました。
reac-router-domのインストール
$ npm i -S react-router-dom
※注) @types
が無い場合、TypeScriptではエラーとなる為、下記コマンド実行
$ npm i -S @types/react-router-dom
Firebaseのインストール
$ npm i -S firebase
Firebase側の設定
次に、Firebase側の設定を行います。
まずはFirebaseのコンソールにアクセス。
https://console.firebase.google.com/?hl=ja
(Googleへのログインが必要です。)
プロジェクトを追加
から画面を開き
プロジェクト名を入力して作成。
今回はチェックは全て無しでOKでしょう。
コンソールに接続したら、左側のサイドバーから
Authentication
を選択します。
ログイン方法を設定
を押下し、メール / パスワード
を有効化します。
これで一旦Firebase側の設定は完了です。
認証ページの作成
SignUp用のページを作成します。
今回は/src
配下にSignUp.tsx
というファイルを作成します。
cssは楽したいのでMaterial-UI
を使用します。
$ npm i @material-ui/core @material-ui/icons
SignUp.tsxに下記コードを書きます。
import React from "react"; import Paper from '@material-ui/core/Paper'; import Input from '@material-ui/core/Input'; import FormControl from '@material-ui/core/FormControl'; import InputLabel from '@material-ui/core/InputLabel'; import Button from '@material-ui/core/Button'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import withStyles, {StyleRules, WithStyles} from '@material-ui/core/styles/withStyles'; import {Theme} from '@material-ui/core/styles/createMuiTheme'; import Checkbox from "@material-ui/core/Checkbox/Checkbox"; import Typography from "@material-ui/core/Typography/Typography"; import Avatar from "@material-ui/core/Avatar/Avatar"; import LockOutlinedIcon from '@material-ui/icons/LockOutlined'; import CssBaseline from "@material-ui/core/CssBaseline/CssBaseline"; const styles = (theme: Theme): StyleRules => ({ main: { width: 'auto', display: 'block', // Fix IE 11 issue. marginLeft: theme.spacing.unit * 3, marginRight: theme.spacing.unit * 3, [theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: { width: 400, marginLeft: 'auto', marginRight: 'auto', }, }, paper: { marginTop: theme.spacing.unit * 8, display: 'flex', flexDirection: 'column', alignItems: 'center', padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`, }, avatar: { margin: theme.spacing.unit, backgroundColor: theme.palette.secondary.main, }, form: { width: '100%', // Fix IE 11 issue. marginTop: theme.spacing.unit, }, submit: { marginTop: theme.spacing.unit * 3, }, }); interface Props extends WithStyles<typeof styles> { } // SignUp用のinterface interface SignUpObject { username: string, email: string, password: string, error: any | null, } class SignUp extends React.Component<Props, SignUpObject> { constructor(props: any) { super(props); this.state = { username: "", email: "", password: "", error: null }; } // input要素が変更された時のハンドリング private handleChange = (event: any) => { }; // submitされた時の挙動 private handleSubmit = async (event: any) => { }; // エラーハンドリング private handleError = () => { }; render() { const state = this.state; const {classes} = this.props; return ( <main className={classes.main}> <CssBaseline /> <Paper className={classes.paper}> <Avatar className={classes.avatar}> <LockOutlinedIcon/> </Avatar> <Typography component="h1" variant="h5"> Sign in </Typography> <form onSubmit={this.handleSubmit} className={classes.form}> {state.error !== null && this.handleError()} <FormControl margin="normal" required fullWidth> <InputLabel htmlFor="username">UserName</InputLabel> <Input name={"username"} id="username" value={state.username} onChange={this.handleChange}/> </FormControl> <FormControl margin="normal" required fullWidth> <InputLabel htmlFor="email">Email Address</InputLabel> <Input name={"email"} id="email" value={state.email} onChange={this.handleChange}/> </FormControl > <FormControl margin="normal" required fullWidth> <InputLabel htmlFor="password">Password</InputLabel> <Input name={"password"} id="password" type="password" value={state.password} onChange={this.handleChange}/> </FormControl> <FormControlLabel control={<Checkbox value="remember" color="primary"/>} label="Remember me" /> <Button type="submit" fullWidth variant="contained" color="primary" className={classes.submit} > Sign in </Button> </form> </Paper> </main> ); } } export default withStyles(styles)(SignUp);
これで認証ページができました。
style周りはMaterial-UIのPage Layout Examplesから拝借してきたので
少しごちゃごちゃしていますが、ご勘弁を。
ハンドリング用の関数については次の記事で書きます。
routingの実装
次に、react-router-dom
を使用してroutingを完成させます。
App.tsx
をこんな感じに編集。
+ import React from 'react'; + import {BrowserRouter, Link, Route} from "react-router-dom"; import logo from './logo.svg'; import './App.css'; + import SignUp from "./SignUp"; + import AppBar from "@material-ui/core/AppBar/AppBar"; + import Toolbar from "@material-ui/core/Toolbar/Toolbar"; + import Button from "@material-ui/core/Button/Button"; + const App = () => ( + <BrowserRouter> + <div> + <AppBar position="static" color="default"> + <Toolbar> + <Button variant="contained"> + <Link to={'/'}>home</Link> + </Button> + <Button variant="contained"> + <Link to={'/signup'}>signup</Link> + </Button> + </Toolbar> + </AppBar> + <Route exact path='/' component={DefaultApp}/> + <Route path='/signup' component={SignUp}/> + </div> + </BrowserRouter> + ); + class DefaultApp extends React.Component { render() { return ( <div className="App"> <header className="App-header"> ...
classであるDefaultApp内は、元のApp.tsx
でrenderしていた内容と同一です。
Appとしてexportしている箇所にroutingを実装しています。
詳しくは解説しませんが、知りたい方は下記記事参照。
react-router@v4を使ってみよう:シンプルなtutorial - Qiita
これでメイン画面にボタンが2つ表示され、signup
をクリックすると
認証ページに飛ぶことが確認できるはずです。
とりあえず今回はここまで。
続きの記事も書き溜めているので、随時公開していきます。
現場からは以上です。