フロントエンド初学者が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はまた別で。