はじめに
React×Redux×Firebaseを使って認証機能を実装しました。その際にReducksパターンを使用して、
状態遷移を実装したので、その時に使用したコードと、その簡単な動きをメモしておきます。
一連のアプリの中の認証部分のみをトリミングして記事にしているので動作が保証できません。
実装の流れを把握していただけると幸いです。
もしこのコードを実装して「動かないぞ!!」ってなったら
コメントいただけると、なにか手助けができるかもです。
↓かなり参考にさせていただいたサイト
実装手順
ReactやFirebaseが導入されている前提で話を進めていきます。
長くなってしまったので2つに分けて投稿します。
以下の順番で話を進めていきます。
- ReactとReduxを連携
- StoreをReduxで管理
- Firebase Authenticationの実装
- ログイン画面の実装【その1ではここまで!】
- Reducksパターンを導入
- Operationの実装(Firebase)
- ActionとReducerの実装
- Selectorの実装
- ログイン状態によって遷移先を変える
使用したバージョン
macOS Big Sur Version 11.5.2 npm 7.20.3 node 15.11.0 react 17.0.2 react-redux 7.2.4 redux-thunk 2.3.0 reselect 4.0.0 connected-react-router 6.9.1
目標とする動き
- メイン画面へのアクセスがあったら、ログイン状態を確認し遷移先を変える。
- ログインされたら、その回数をカウントする。
この二つを実装していきます。
実装は実用的ではないですが、
Reducksパターンの概要をコードをおって掴むことができると思います!
ファイルパスは適宜変えていただけると幸いです。
それでは!→→→
ReactとReduxを連携
Reduxを簡単に説明すると”State”をどのコンポーネントからでもアクセスできるように
するためのツール的なイメージです。今回は詳細を省かさせていただきます。
Reactの環境にReduxモジュールを入れただけでは、Reduxの動作は何もおこりません。
そのためReactとReduxを導通させるためのコードをindex.jsに記載します。
ProviderとConnectedRouterでAppコンポーネントをラップします。
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import createStore from './reducks/store/store';
import * as History from 'history';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
const history = History.createBrowserHistory();
export const store = createStore(history);
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>,
document.getElementById('root')
);
App.js
import React from 'react';
import {Route, Switch} from 'react-router';
const App = () => {
return (
<div className="App">
<Switch>
<Route exact path="/SignIn" component={SignIn}/>
</Switch>
</div>
);
}
export default App;
StoreをReduxで管理
次にStoreで管理したいStateを記述していきます。
今回は二つ(UserとRoute)です。
これらをcombineReducersの第一引数に取ることで
無事にReduxとReactを導通することができます。
ちなみに、historyモジュールによりReactのルーティングもStoreで管理することができます。
また、外部API(Firebase)を利用するということで、Reduxの同期的な状態遷移に
非同期通信を導入しますので、その対応もしないといけません。
thunkを導入することで、非同期処理を実現することができます。
Store.js
import { createStore as reduxCreateStore, combineReducers, applyMiddleware } from 'redux';
import { connectRouter, routerMiddleware } from 'connected-react-router';
import thunk from 'redux-thunk';
import { UserReducer } from '../users/reducers';
export default function createStore(history) {
return reduxCreateStore(
combineReducers({
router: connectRouter(history),
user: UserReducer,
}),
applyMiddleware(
routerMiddleware(history),
thunk
)
)
}
Firebase Authenticationの実装
あらかじめFirebaseConsoleで認証機能部分を許可しておきます
ログイン画面の実装
import React from 'react';
import Avatar from '@material-ui/core/Avatar';
import Button from '@material-ui/core/Button';
import CssBaseline from '@material-ui/core/CssBaseline';
import TextField from '@material-ui/core/TextField';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
import Link from '@material-ui/core/Link';
import Grid from '@material-ui/core/Grid';
import Box from '@material-ui/core/Box';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { signInWithEmailAndPassword, signInWithGoogle } from '../reducks/users/operations'
import GoogleButton from '../components/GoogleButton';
import { useCallback } from 'react';
const useStyles = makeStyles((theme) => ({
paper: {
marginTop: theme.spacing(8),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main,
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
const SignIn = () => {
const classes = useStyles();
const [mailAddress, setMailAddress] = useState("")
const [password, setPassword] = useState("")
const inputEmail = useCallback((e) => {
setMailAddress(e.target.value)
}, [mailAddress])
const inputPassword = useCallback((e) => {
setPassword(e.target.value)
}, [password])
// Reduxとの連携
const dispatch = useDispatch();
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
onChange={inputEmail}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
onChange={inputPassword}
/>
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<Button
// type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={() => dispatch(signInWithEmailAndPassword(mailAddress, password))}
>
Sign In
</Button>
</form>
</div>
</Container>
)
}
export default SignIn;
サインインの画面コードはMaterialUIさんが配布しているものを拝借させていただきました。
Buttonの属性にonClickがあるため、クリックされた時にReducksパターンである、
operation.jsのsignInWithEmailAndPasswordメソッドが起動されることになります。
詳細は後述。
コメント