本題
Gatsbyとfirebaseを組み合わせて開発中にdevelopはできるのにbuildするとErrorになって困ったことありませんか?
この記事の解決策が皆様のお役に立てたら幸いです。
筆者の環境
- MacBook Pro 13inch 2018
- "gatsby": "^4.6.1",
- "firebase": "^8.10.0",
- "node" :"v16.13.0"
通常通り開発すると…
通常ですと以下の様な感じでに開発されているのではないでしょうか
// firebase.js
import firebase from "firebase"
const firebaseConfig = {
apiKey: "your apiKey",
authDomain:"your authDomain",
projectId: "your projectId",
storageBucket: "your storageBucket",
messagingSenderId: "your messagingSenderId",
appId: "your appId",
};
// Initialize Firebase
export const initFirebase = () => {
if (firebase.apps.length === 0) {
firebase.initializeApp(firebaseConfig);
}
};
// App.js
import React from "react";
import { initFirebase } from "./firebase.js"
export default function AppPage() {
initFirebase()
return <a>HogeHoge</a>;
}
これで npm run develop
を実行すると
問題なくhttp://localhost:8000/
で開発サーバーが立ち上がると思います。
しかし、npm run build
を実行すると以下の様なエラーが出ます。
ERROR #95313
Building static HTML failed for path "/App/"
See our docs page for more info on this error: https://gatsby.dev/debug-html
700 | * limitations under the License.
701 | */
> 702 | registerFunctions(firebase, fetch.bind(self));
| ^
703 | firebase.registerVersion(name, version);
704 | //# sourceMappingURL=index.esm.js.map
705 |
WebpackError: ReferenceError: self is not defined
何が問題なのか
以下のページに同じ悩みを持つ人が集まってました。
コメントの一部をGoogle翻訳にかけてみると…
Firebaseには、ウィンドウオブジェクトを参照するinitializeAppメソッドがあります。 Gatsbyは、ビルドフェーズでウィンドウオブジェクトにアクセスできません。したがって、そのオブジェクトにアクセスできるようになるまで、Firebaseの初期化を遅らせる必要があります。コンポーネントのcomponentDidMountライフサイクルフックに動的にインポートすると、これが保証されます。しかし、これで問題の解決は終わりではありません。
私は非エンジニアなので詳しいことはよくわかりません!
ただ一つ言えること、それは…
Gatsbyとfirebaseはbuild時に相性が悪い!
とザックリ理解しました。
(動けばいいのよ動けば…)
解決策
まずfirebase.jsを書き換えてwindowオブジェクトがなければ初期化されないようにします。
//firebase.js
import firebase from "firebase/app";
import "firebase/auth";
const firebaseConfig = {
apiKey: "your apiKey",
authDomain:"your authDomain",
projectId: "your projectId",
storageBucket: "your storageBucket",
messagingSenderId: "your messagingSenderId",
appId: "your appId",
};
let instance = null;
export default function getFirebase() {
if (typeof window !== "undefined") {
if (instance) return instance;
instance = firebase.initializeApp(firebaseConfig);
return instance;
}
return null;
}
また下記の様にuseFirebase.jsを新たに作成してください。
初期化したfirebaseインスタンスをStateにいれてます。
//useFirebase.js
import { useEffect, useState } from "react";
import getFirebase from "./firebase"; // import our getFirebase function
export default function useFirebase() {
const [instance, setInstance] = useState(null);
useEffect(() => {
setInstance(getFirebase());
}, []);
return instance;
}
firebaseを使うには以下のようにしてください。
これでいつもと同じようにfirebaseを利用することができます。
// App.js
import React from "react";
import useFirebase from "./useFirebase";
export default function AppPage() {
const firebase = useFirebase();
useEffect(() => {
if (!firebase) return;
return firebase.auth().onAuthStateChanged((user) => {
console.log('User:', user);
});
}, [firebase]);
// ...
}
最後にgatsby-node.jsに以下を記述します。
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /firebase/,
use: loaders.null(),
},
],
},
});
}
};
何をしているかって?
一部google翻訳を掛けて引用します。
サーバーのレンダリング中にfirebaseモジュールをダミーモジュールに置き換えるためにWebpack構成をカスタマイズします。
たぶんfirebaseが悪さしないようにダミーに置き換えてる…と思う
最後にもう一度言おう
動けば良いのよ動けば…
これでnpm run build
してみてください!
きっと成功するはずです!
最後に
私はここに躓いて丸2日ほど時間を費やしてしまいました。
皆様の一助となりましたら幸いです。
また、解決策の部分など詳しい方いらっしゃいましたら
コメント欄でご指摘ご教授いただけないでしょうか?
よろしくお願いいたします。
参考サイト