フロントエンド初学者がNext.js + Typescript + FireStore + Material-UI + SSGでサイト作るまでのメモ
Next.jsの使用したサンプル
yarn create next-app --example with-typescript
追加したモジュール
yarn add firebase
yarn add @material-ui/core
yarn add @material-ui/icons
yarn add dotenv
dotenvは使わなくて良いみたい。
Firestoreから取得したデータを型変換する仕組みがFirestore側に標準である。
firebase. firestore. FirestoreDataConverter < T >
export type Word = {
description: string;
word: string;
};
// コンバート情報の定義
export const wordConverter = {
toFirestore(word: Word): firebase.firestore.DocumentData {
return {
description: word.description,
word: word.word,
};
},
fromFirestore(
snapshot: firebase.firestore.QueryDocumentSnapshot,
options: firebase.firestore.SnapshotOptions
): Word {
const data = snapshot.data(options)!;
return {
description: data.description,
word: data.word,
};
},
};
const wordList: Word[] = [];
await db.collection("words")
.withConverter(wordConverter) // コンバート指定
.get()
.then((ss) => {
ss.forEach((s) => {
wordList.push(s.data());
});
});
Firestoreから取得した値でフィールドがない場合、コンバート時にはundefined
になる。
getServerSideProps
の戻り値はシリアライズできることが条件になっているため、コンバート時にundefined
チェックを行う。
// 型エイリアス
export type Word = {
alphabet?: string;
description: string;
word: string;
};
// コンバート処理
export const wordConverter = {
toFirestore(word: Word): firebase.firestore.DocumentData {
return {
alphabet: word.alphabet === undefined ? null : word.alphabet,
description: word.description,
word: word.word,
};
},
fromFirestore(
snapshot: firebase.firestore.QueryDocumentSnapshot,
options: firebase.firestore.SnapshotOptions
): Word {
const data = snapshot.data(options)!;
return {
alphabet: data.alphabet === undefined ? null : data.alphabet,
description: data.description,
word: data.word,
};
},
};
Material-UIをSSRで使おうとしたらエラーが出た。
Warning: Prop `className` did not match. Server: "MuiTypography-root makeStyles-text-35 MuiTypography-body1" Client: "MuiTypography-root makeStyles-text-1 MuiTypography-body1"
Server側とClient側でCSSが共有化できていないため発生したようだ。
Material-UIをSSRで使うには設定が必要。
Server Rendering
_document.tsxを追加することで解決。
実装は先コメントのSetting up a Blog with Next.js React, Material-UI and Typesctiptでできた。
_document.tsxで行ける理由は後で確認する。
getServerSideProps
をTypescriptで使う場合のサンプル
改行コードが入っているテキストを改行したまま表示する方法
CSS(@material-ui/core/styles)でwhiteSpace: "pre-wap"
したが、改行されない。
スタイルシートは正しくかけているように見えるけど、何か間違えているのか。
Firestoreから取得した値を確認したらエスケープ処理されていた。
"年間経常収益\\nTEST"
実装箇所で、replaceしてあげたら正しく改行された。
{word.replace("\\\n", "\n")}
String.prototype.replace()
は1つ目しか対象にならない。
const p = 'The quick brown fox jumps over the lazy dog. If the dog reacted, was it really lazy?';
console.log(p.replace('dog', 'monkey'));
// expected output: "The quick brown fox jumps over the lazy monkey. If the dog reacted, was it really lazy?"
String.protptype.replaceAll()
はすべて対象となるようだが、エラーで使えない。
Property 'replaceAll' does not exist on type 'string'. Do you need to change your target library? Try changing the `lib` compiler option to 'esnext' or later.ts(2550)
String.prototype.replaceAll()
はECMA-262で使用可能となるようだ。
https://tc39.es/ecma262/#sec-string.prototype.replaceall
tsconfig.jsonのlibを変更すれば対応できるのか?
"compilerOptions": {
"lib": ["dom", "es2017"],
"module": "esnext",
},
各ブラウザバージョンも比較的最新版でないと利用できないAPIのため、別の方法が良さそう。
正規表現を使って解決
{word.replace(/\\n/g, "\n")}
Firestoreで検索かつソートを行う際に異なるフィールドを指定する場合は、複合インデックスを作成する必要がある。
await db
.collection("words")
.withConverter(wordConverter) // objectコンバータで変換して型安全に利用する。
.where("syllabary", "==", char)
.orderBy("word")
Firebase Hostingにデプロイするためにビルドしようとしたらエラーになった。
yarn run export
Error occurred prerendering page "/_documents". Read more: https://err.sh/next.js/prerender-error
TypeError: originalRenderPage is not a function
\_documents.tsx
を\_document.tsx
に変更することでビルドできた。
firebase deploy
時にエラーが発生。
無料プランだからNGとのこと。
Error: Server Error. getaddrinfo EAI_AGAIN firebaserules.googleapis.com
SSGだからAPI実行はされないと思っていたが何らかの設定不備?
しばらくして再度firebase deploy
したら今度は成功。
特に変更したことはなく理由は定かではない。
作ったもの。
ソース
SEOはまた別で。