お問い合わせ機能実装の悩みをHyperFormを使って解決することができたので、今回はその詳細をまとめます。
きっかけ
現在、プライベートでアプリ開発をしており、アプリのお問い合わせ機能をランディングページとアプリ内に用意する必要がありました。
当初、開発検討していた内容は以下のようなものでした。
- アプリとランディングページ上に、お問い合わせページを用意
- お問い合わせをもらったらその内容をfirestoreに保存
- firestoreへの書き込みをトリガーにお問い合わせしてくださった方へお問い合わせ内容を転記した自動返信を実行し、
- 自分もその問い合わせに気づけるようにSlackに飛ばし、一覧管理できるようにスプレッドシートに格納する。
しかし、これをやるには、
firestoreからメール送信する際の実装やSlackやスプレッドシートに反映させる実装への理解が必要で、
アプリの本機能の開発に集中したかった私は、Googleフォームを選択しました。
Googleフォームのメリット・デメリット
Googleフォームを使うと、簡単にお問い合わせフォームを用意でき、スプレッドシートにも、回答内容は自動反映されるし、メールも自動返信することは可能です。
一方で、フォームのUIはGoogle側で固定されており、カラーコードを合わせたとしても、アプリのデザインとは明らかにことなり、
それなりに合わせようとすると、いろいろとまたコードを書く必要がありました。
HyperFormに出会う
Googleフォームの埋め込むのはだいぶイケてないけど、まあ仕方ないか。。。と思っていたところ、HyperFormに出会いました。
HyperFormはFormタグにactionを追加することで、上記のような私の悩みを解決することができました。
具体的にできることは以下の3つです。
- お問い合わせをもらったことをトリガーにお問い合わせ内容を転記した自動返信を実行
- 任意のSlackチャネルへお問い合わせ内容を通知
- お問い合わせ内容を一覧管理できるようにスプレッドシートに格納
また、formタグで実装するので、Reactのコンポーネントとも組み合わせて利用ができ、新たな実装を追加しなくてよいという点もかなりよかったです。
実装イメージ
もともとReact hook form とChakraUIの組み合わせて、Inputのコンポーネントを実装していたため、お問い合わせについてもそのコンポーネントの再利用をし、submit発火時にReact hook formのバリデーションがきくように実装しました。
//テキストインプット用の子コンポーネント
import {
FormLabel,
FormControl,
Input,
Box,
FormErrorMessage,
} from "@chakra-ui/react";
export const InputTextForm = (props) => {
const {
register,
id,
label,
isInvalid,
placeholder,
isReadOnly,
isRequired,
list,
width = "300px",
} = props;
return (
<div>
<FormControl
id={id}
alignItems="center"
isInvalid={isInvalid}
isRequired={isRequired}
display="flex"
>
<FormLabel width="200px" htmlFor={id} fontWeight="bold">
{label}
</FormLabel>
<Box>
<Input
{...register}
type="text"
placeholder={placeholder}
isReadOnly={isReadOnly}
list={list}
width={width}
/>
<FormErrorMessage>{isInvalid && isInvalid.message}</FormErrorMessage>
</Box>
</FormControl>
</div>
);
};
// お問い合わせに使う親コンポーネント
import { Box, Stack, Button, SimpleGrid, Flex, Center } from "@chakra-ui/react";
import { InputTextForm } from "../molecules/InputTextForm";
import { useForm } from "react-hook-form";
export const Home = () => {
const onSubmit = (_,e) => {
e.target.submit();
reset();
};
const { register, handleSubmit, formState, reset } = useForm();
return (
<Center mb={5}>
<form
noValidate
method="post"
action="https://hyperform.jp/api/{HyperFormのID}"
onSubmit={handleSubmit(onSubmit)}
target="_blank"
>
<Flex direction="column">
<Box
px={{
base: "4",
md: "15",
}}
width="5xl"
display="flex"
justifyContent="center"
>
<Box
bg="white"
rounded="md"
p={{ base: "4", md: "8" }}
shadow="base"
my="4"
width="2xl"
>
<Stack spacing="6" p="4">
<InputTextForm
id="name"
label="お名前"
isInvalid={formState.errors?.name}
register={register("name", {
maxLength: {
value: 20,
message: "20文字以下で入力してください",
},
required: "必須入力項目です",
})}
isRequired
/>
</Stack>
<SimpleGrid display="flex" justifyContent="center">
<Button
bg="green"
color="white"
size="md"
mt="50px"
type="submit"
width="100px"
>
送信
</Button>
</SimpleGrid>
</Box>
</Box>
</Flex>
</form>
</Center>
);
};
export default Home;
実装においてつまったところ
当初、Buttonのtypeをsubmitとし、formにmethodとactionを追加したらうまくいくものだと思っていました。
その方法でも一通りは動くのですが、
それだけだと、React hook formで追加しているバリデーションが発火しないまま、フォーム送信が実行されてしまうという事象が発生しました。
現役エンジニアのメンターさんに相談したところ、
明示的にフォームをサブミットしてやる必要があり、そのための記述が e.target.submit() になるとのことでした。
React hook form を組み合わせて発火させるためには以下の部分一工夫が必要でした。
const onSubmit = (_,e) => {
e.target.submit();
};
なんとかフォームの完成
上記のコードの組み合わせで以下のようなフォームを簡単に実装できました。感動!
個人開発でお問い合わせフォームの実装に漠然として悩みを抱えている方はぜひ、使ってみていただきたいです。