「React Hook Form」でフォームを簡単に作る

Next.js(React)でログイン機能を実装していて、フォーム作成に苦戦していました。 そもそも、Reactを全然理解していないので、フォームの値はどうやって取得すればいいのか?みたいなところからわかりませんでした。 今回はReact+TypeScriptでフォームを作成するのにReact Hook Formがとても便利だったので、紹介します。 React Hook Formとは 公式サイトを見ると「高性能で柔軟かつ拡張可能な使いやすいフォームバリデーションライブラリ」らしいです。 詳細は以下公式サイトを見てください。 React Hook Form さっそく実装をする まずはReact Hook Formをインストールします。 以下コマンドをプロジェクト配下で実行します。 npm install react-hook-form インストールしたら、importしてフォームの値をサポートするためのFormData型を作成します。 import { useForm } from "react-hook-form"; type FormData = { username: string; password: string; }; 今回はログイン用のフォームを作成するために、string型のusernameとpasswordを用意しました。 次に、useFormを使ってフォーム作成に必要なメソッドを受け取ります。 import { useForm } from "react-hook-form"; type FormData = { username: string; password: string; }; export default function Login() { const { register, handleSubmit, watch, errors } = useForm<FormData>(); } useForm<FormData>()のように書いてフォームで使う型を設定できます。 constで定義されているメソッドについては以下にまとめました。 register: input/selectのRefとバリデーションルールをReact Hook Formに登録する handleSubmit: フォームバリデーションに成功するとフォームデータを渡す watch: 指定されたinput/inputsを監視し、その値を返す errors: 各inputのフォームのエラーまたはエラーメッセージを含む これらのメソッドを使って、フォームを作成します。 import { useForm } from "react-hook-form"; type FormData = { username: string; password: string; }; export default function Login() { const { register, handleSubmit, watch, errors } = useForm<FormData>(); const onSubmit = (data: FormData): void => console.log(data); console.log(watch("username")); console.log(watch("password")); return ( <form onSubmit={handleSubmit(onSubmit)}> <div className="filed"> <label className="label">username</label> <input name="username" placeholder="username" ref={register({ required: true })} /> {errors.username && "usernameを入力してください。"} </div> <div className="filed"> <label className="label">password</label> <input name="password" placeholder="password" ref={register({ required: true })} /> {errors.password && "passwordを入力してください。"} </div> <div className="filed"> <button>login</button> </div> </form> ); } フォームの流れにそって解説します。 フォームのinputで作成したFormDataを使うためにはname属性に設定します。また、ref={register({ required: true })}のように書くことで、inputは入力必須にしています。 {errors.username && "usernameを入力してください。"}はusernameがバリデーションエラーになったら表示されます。最後に、フォームに入力をしてボタンを押してバリデーションエラーが発生していない場合、<form onSubmit={handleSubmit(onSubmit)}>が動いて、const onSubmit = (data: FormData): void => console.log(data);が実行されてコンソールに入力された値が表示されます。 フォームに入力がない状態でボタンを押すと以下のような挙動をします。 console.log(watch("username"));はusernameに入力された値が変わるとその都度コンソールに表示します。 ...

May 26, 2020 · 1 min · 187 words · Yu

HugoでTwitterカードを表示させる

これまでこのブログはTwitterなどにリンクを貼った際にサムネイル画像を表示できていなかったので、Twitterカードで画像を表示できるようにしました。また、Twitterだけではなく、Slack(コミュニケーションツール)などでも画像が表示できるにしています。 Twitterカードとは サルワカくんの記事が分かりやすかったので、詳しく知りたい人は以下記事を読んでみてください。 【2020年版】Twitterカードとは?使い方と設定方法まとめ 簡単に説明すると、Twitterでよく見かけるURLを貼ったときに表示されるやつです。 Twitterカード表示対応 僕が現在使っているHugoのテンプレートはBeautiful Hugoなので、このテンプレートでの対応方法を書きます。 今回重要なファイルは2つです。 themes/beautifulhugo/layouts/partials/seo/twitter.html themes/beautifulhugo/layouts/partials/seo/opengraph.html ファイルの中身を以下に書きます。 // themes/beautifulhugo/layouts/partials/seo/twitter.html {{- with .Title | default .Site.Title }} <meta name="twitter:title" content="{{ . | truncate 70 }}" /> {{- end }} {{- with .Description | default .Params.subtitle | default .Summary }} <meta name="twitter:description" content="{{ . | truncate 200 }}"> {{- end }} {{- with .Params.share_img | default .Params.image | default .Site.Params.logo }} <meta name="twitter:image" content="{{ . | absURL }}" /> {{- end }} <meta name="twitter:card" content="summary" /> {{- with .Site.Author.twitter }} <meta name="twitter:site" content="@{{ . }}" /> <meta name="twitter:creator" content="@{{ . }}" /> {{- end }} // themes/beautifulhugo/layouts/partials/seo/opengraph.html {{- with .Title | default .Site.Title }} <meta property="og:title" content="{{ . }}" /> {{- end }} {{- with .Description | default .Params.subtitle | default .Summary }} <meta property="og:description" content="{{ . }}"> {{- end }} {{- with .Params.share_img | default .Params.image | default .Site.Params.logo }} <meta property="og:image" content="{{ . | absURL }}" /> {{- end }} {{- with .Site.Params.fb_app_id }} <meta property="fb:app_id" content="{{ . }}" /> {{- end }} <meta property="og:url" content="{{ .Permalink | absLangURL }}" /> <meta property="og:type" content="website" /> <meta property="og:site_name" content="{{ .Site.Title }}" /> ここでみてほしいのは、.Params.share_imgです。 Beautiful Hugoでは各記事のFront Matter(mdファイル内の一番上にtitleとかdateが書いてあるところ)に、share_img: "表示させたい画像のパス"のように書いてあげればTwitterカードに画像を表示できます。 ...

May 22, 2020 · 2 min · 215 words · Yu

Go製APIから「fetch」するのにハマった

Next.js(React)+Goで個人開発をしていて、FetchAPIを利用してGoでローカル環境にたてたAPIをNext.jsで呼び出す際に、値が取れずにはまりました。 今回はどうやって解消したのか記事にしました。 今回発生したエラー Next.jsでfetchを使ってPOSTした際に、以下のようなエラーが発生。 Error serializing `.postData` returned from `getServerSideProps` in "/posts/get-api". Reason: `object` ("[object Promise]") cannot be serialized as JSON. Please only return JSON serializable data types. ようわからんけど、APIから値が取れてないみたいでした。 Next.jsからfetchする際のコードは以下のようにしていました。 export const getServerSideProps: GetServerSideProps = async () => { const url = "http://localhost:8080/login"; ← APIはローカルの8080番ポートにたてていました。 let postData = {}; await fetch(url, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/json charset=utf-8", }, body: JSON.stringify({ username: "yagiyu", password: "miran" }), }).then(function (response) { console.log(response); postData = response.json(); }); return { props: { postData, }, }; }; また、上記を実行した時のレスポンスが以下で、APIと通信はできたが、値を取れませんでした。 Response { size: 0, timeout: 0, [Symbol(Body internals)]: { body: PassThrough { _readableState: [ReadableState], readable: true, _events: [Object: null prototype], _eventsCount: 2, _maxListeners: undefined, _writableState: [WritableState], writable: false, allowHalfOpen: true, _transformState: [Object], [Symbol(kCapture)]: false }, disturbed: false, error: null }, [Symbol(Response internals)]: { url: 'http://localhost:8080/login', status: 200, statusText: 'OK', headers: Headers { [Symbol(map)]: [Object: null prototype] }, counter: 0 } } ターミナルからcurlを実行すると、以下のように成功していました。 ...

May 19, 2020 · 2 min · 217 words · Yu

CSSの「@media」でレスポンシブ対応させる

以前に僕のブログを見たことがある人にはわかると思いますが、サムネイル画像がなかったり、文字のサイズ、余白などが違ったりとデザインをガラッと変えました! デザインについて考えると、どんどん深みにはまって正解がまったくわからなかったです。(笑) CSSで@mediaを使ってデバイスの大きさごとに表示を調整したので、その記事を書きます。 @mediaとは @mediaを使うことで、各デバイスや画面サイズごとに適用するCSSを指定できます。 レスポンシブにする場合は必須みたいです。(CSSは本当によくわからない。。) 使い方 以下のような感じで使うことができます。 @media (max-width: 767px){ img { width: 100%; height: auto; } } @media (min-width: 768px){ img { width: 277px; height: 145px; } } (max-width: 767px)はデバイスの画面幅が767px以下だったら、{}のCSSを適用します。 (min-width: 768px)はデバイスの画面幅が768px以上だったら、{}のCSSを適用します。 これは例としてあげていて、レスポンシブ対応させるときは画像サイズを%やautoを使って指定することでいい感じにできます。 767pxを画面幅の境目にしている理由として、今のパソコンやスマホ、タブレットのサイズを考えると、767pxのサイズに合わせる見やすくできるそうです。 @media screen and (max-width: 767px)のような書き方をしているサイトが多数ありますが、screenとはスクリーン画面ではなく、プリンターまたは音声機器以外のデバイスすべてなので、わざわざscreenをつける必要はないです。 まとめ レスポンシブ対応させるときは@mediaを使って、デバイス毎にCSSを変更させましょう。

May 8, 2020 · 1 min · 43 words · Yu

MongoDB 入門

最近、MongoDBを使うことがあり、NoSQLをはじめて使いました。MongoDBを触ったことがない人のために書きます。 MongoDBインストール&起動 今回はDockerHubからMongoDBのコンテナイメージをインストールします。 $ docker pull mongo インストールができたら、以下コマンドでコンテナを起動します。 $ docker run --name mongo-sample -d mongo -dはバックグランドで起動することを意味してます。 起動したコンテナ内に以下コマンドで入ります。 $ docker exec -it mongo-sample bash MongoDBのシェル起動 MongoDBのコンテナ内で以下コマンドを実行します。 $ mongo シェルを起動したら、環境準備は完了です。 基礎的な用語解説 MongoDBをさわっていく上で、イメージしやすくするために、Oracleの用語と比較します。 MongoDB Oracle データベース データベース コレクション テーブル ドキュメント 行(レコード) フィールド 列(カラム) テストデータ作成 以下コマンドをシェルで入力して、テストデータを作成します。 > db.foods.insert({name:'apple', price:50, producer:['sato']}); > db.foods.insert({name:'meat', price:100, producer:['suzuki']}); > db.foods.insert({name:'fish', price:120, producer:['kaneko', 'kitamura']}); 以下コマンドでDBの情報をみることができます。 > db.stats() { "db" : "test", "collections" : 1, "views" : 0, "objects" : 3, "avgObjSize" : 86, "dataSize" : 258, "storageSize" : 20480, "numExtents" : 0, "indexes" : 1, "indexSize" : 20480, "scaleFactor" : 1, "fsUsedSize" : 28014751744, "fsTotalSize" : 62725623808, "ok" : 1 } testデータベースに"collections" : 1となっていて、foodsコレクションが作成されて、"objects" : 3からドキュメントが3つ作成されていることが分かります。 ...

April 30, 2020 · 3 min · 443 words · Yu