【Next.js】Next.js + React Hooks + Cloud Functions for Firebaseでお問い合わせフォームを実装
はじめに
こんにちは、がんがんです。
Webアプリとかを作っているとお問い合わせフォームが必要になることがあるかと思います。簡単に実装できればいいものの、環境に合わせてお問い合わせフォームの実装は様々なやり方が存在しています。
そこで、今回はCloud Functions for Firebaseを用いてお問い合わせフォームの実装を行っていきたいと思います。
目次
目的
Cloud Functions for Firebaseを利用してお問い合わせフォームを作成していきます。
フロント側はNext.js + Material-ui + React Hooksを使用して実装していきました。
参考
Cloud Functions for firebase関連
- [firebase] Cloud Functionsを利用したお問い合わせ機能の実装 [react.js] - Qiita
- Vue.js + Firebase functionsでお問い合わせフォームを作成する - Qiita
React Hooks関連
Cloud Functions for Firebaseでのメール機能を実装
Functions側の実装を行っていきます。Firebaseのセットアップについてはこちらを参照ください。
環境(Dockerfile)の準備
フロントとFirebaseの環境が異なるため、Dockerを用いた環境をセットアップします。Cloud FunctionsにおけるNode.jsの環境は8・10・12
です。
Node.js:8は2021年3月にはシャットダウンされるため、これから使用を検討する方はSparkプラン(無料)→Blazeプラン(従量課金)に変更したうえで使用ください。Functionsのバージョンについては公式ドキュメントを参照ください。
firebase.google.com
今回は実験ということでNode.js:8を使用しています。
Dockerfile |
FROM node:8-alpine WORKDIR /usr/src RUN npm i -g firebase-tools \ && rm -rf /var/lib/apt/lists/*
docker-compose |
version: '3' services: app: build: . ports: - 9005:9005 volumes: - ./functions:/usr/src tty: true stdin_open: true command: /bin/sh
9005ポートはfirebase login:ci
用なので、必要がなければ消しても大丈夫です。
次に、firebase cliを使ってセットアップを行います。基本的には勝手にやってくれるので助かります。
# on Docker $ firebase login --no-localhost $ firebase init functions # functions dirが生成後 $ cd functions # package.jsonに"nodemailer"を追加しておく $ yarn install
functionの実装
メールサーバとして使用するgmailや送信先のアドレスをfunctions.config()として設定します。
$ firebase functions:config:set gmail.email="メールサーバーとして使うgmail" gmail.password="メールサーバーとして使うgmailのパスワード" admin.email="問い合わせメールの送信先となるページ管理者のアドレス"
実装は下記の通りです。基本的には参考サイトとほぼ同じです。
index.js |
const functions = require("firebase-functions"); const nodemailer = require("nodemailer"); const gmailEmail = functions.config().gmail.email; const gmailPassword = functions.config().gmail.password; const adminEmail = functions.config().admin.email; // メールサーバの設定 const mailTransport = nodemailer.createTransport({ service: "gmail", auth: { user: gmailEmail, pass: gmailPassword } }); // 管理者用のメールテンプレート const adminContents = data => { return `以下内容でホームページよりお問い合わせを受けました。 お名前: ${data.name} メールアドレス: ${data.email} 内容: ${data.content} `; }; exports.sendMail = functions.https.onCall((data, context) => { // メール設定 let adminMail = { from: gmailEmail, to: adminEmail, subject: "ホームページお問い合わせ", text: adminContents(data) }; // 管理者へのメール送信 mailTransport.sendMail(adminMail, (err, info) => { if(err) { return console.error(`send failed. ${err}`); } return console.log("send success."); }); });
Firebaseへのデプロイは
$ yarn deploy
で大丈夫です(生成されたpackage.jsonのおかげです)。
Next.js + Material-ui + React Hooksでフロント(お問い合わせフォーム)の実装
フロント側の実装にはNext.js + Material-uiを使用します。stateの管理にはReact Hooksを使用していきます。
Firebaseの初期化は別途行う必要がありますので、参考記事を参照してセットアップしてください(ここでは割愛します)。
contact.js |
import React, { useState } from 'react'; import Alert from '@material-ui/lab/Alert'; import Backdrop from '@material-ui/core/Backdrop'; import Button from '@material-ui/core/Button'; import CircularProgress from '@material-ui/core/CircularProgress'; import Container from '@material-ui/core/Container'; import Snackbar from '@material-ui/core/Snackbar'; import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; import { makeStyles } from '@material-ui/core/styles'; import firebase from 'firebase/app'; import 'firebase/functions'; const useStyles = makeStyles((theme) => ({ textField: { display: 'flex', width: '300px', }, contactForm: { display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '100px', }, backdrop: { zIndex: theme.zIndex.drawer + 1, color: '#fff', }, })); export default function Contact() { const classes = useStyles(); const [sendData, setSendData] = useState({ email: '', name: '', content: '' }); const [loading, setLoading] = useState(false); const [snackbarOpen, setSnackBarOpen] = useState(false); const [snackbarInfo, setSnackBarInfo] = useState({ severity: '', message: '' }); // Submit Button const handleSubmit = e => { e.preventDefault(); setLoading(true); let sendMail = firebase.functions().httpsCallable('sendMail'); sendMail(sendData) .then(() => { setSnackBarInfo({ severity: 'success', message: 'お問い合わせありがとうございます。送信完了しました。' }); setSnackBarOpen(true); console.log('Successed send mail.'); setSendData({ email: '', name: '', content: '' }); }) .catch(err => { setSnackBarInfo({ severity: 'error', message: '送信に失敗しました。時間をおいて再度お試しください。' }); setSnackBarOpen(true); console.log(err); }) .finally(() => { setLoading(false); }) }; // Change TextField const handleChange = e => { setSendData({ ...sendData, [e.target.name]: e.target.value }); }; // Close SnackBar const handleSnackBarClose = (event, reason) => { if (reason === 'clickaway') { return; } setSnackBarOpen(false); } return ( <main> <Container maxWidth="sm" className={classes.contactForm}> <Typography variant="h4" component="h1" gutterBottom> お問い合わせ </Typography> <TextField name="email" label="メールアドレス" type="mail" required className={classes.textField} value={sendData.email} onChange={handleChange} /> <TextField name="name" label="お名前" type="text" required className={classes.textField} value={sendData.name} onChange={handleChange} /> <TextField name="content" label="お問い合わせ内容" required multiline rows="8" margin="normal" variant="outlined" className={classes.textField} value={sendData.content} onChange={handleChange} /> <Button variant="contained" color="primary" type="submit" className={classes.textField} onClick={handleSubmit} > 送信 </Button> <Backdrop className={classes.backdrop} open={loading}> <CircularProgress color="inherit" /> </Backdrop> <Snackbar open={snackbarOpen} autoHideDuration={6000} onClose={handleSnackBarClose}> <Alert onClose={handleSnackBarClose} severity={snackbarInfo.severity}> {snackbarInfo.message} </Alert> </Snackbar> </Container> </main> ); }
おわりに
今回はCloud Functions for Firebaseを用いたお問い合わせフォームの実装を行っていきました。比較的簡単に実装できるのは非常に助かります。
現状のお問い合わせフォームはバリデーションなどを行っていません。そのため、React-Hooks-Formあたりを使ったフォームの作成を今後試してみようかと思います。