Remix | ReactでreCaptcha Enterpriseを使う
RemixでreCaptcha Enterpriseを使う際に少しだけ工夫したのでそのメモです。
(結果)クライアントサイドを判断してreCaptchaを導入するCustom Hookの作成
import { useEffect, useState } from "react";
import { recaptchaReady } from "./security";
const useClient = (): boolean => {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
if (typeof window !== "undefined") setIsClient(true);
}, []);
return isClient;
};
const useRecaptcha = (recaptchaSiteKey: string): string => {
const isClient = useClient();
const [token, setToken] = useState("");
const recaptchaScriptUrl = `https://www.google.com/recaptcha/enterprise.js?render=${recaptchaSiteKey}`;
useEffect(() => {
if (isClient) {
const head = document.getElementsByTagName("head")[0] as HTMLElement;
const scriptUrl = document.createElement("script");
scriptUrl.src = recaptchaScriptUrl;
head.appendChild(scriptUrl);
const intervalId = setInterval(async () => {
if (typeof grecaptcha !== "undefined") {
const token = await recaptchaReady(recaptchaSiteKey);
setToken(token);
clearInterval(intervalId);
}
}, 1000);
}
}, [isClient]);
return token;
};
export { useClient, useRecaptcha };
SSRだとエラー(後述)が出たままになるのでクライアントを判定
<script>を動的にHTMLに追加
setIntervalで読み込まれるまでループ
元々のエラー
最初はroot.tsxに<script>を追加したのですが、以下のエラーが表示されていました。
react-dom.development.js:67 Warning: Prop `src` did not match. Server: "https://www.gstatic.com/recaptcha/releases/2W_gRz39xX8G13fM-OdyQPlc/recaptcha__ja.js" Client: "https://www.google.com/recaptcha/enterprise.js?render=<Site Key>"
at script
at head
at html
at App (http://localhost:3000/build/root-MYD2FZ3A.js:29:20)
at RemixRoute (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:2536:3)
at Routes2 (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:2519:7)
at Router (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:669:15)
at RemixCatchBoundary (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:1029:10)
at RemixErrorBoundary (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:954:5)
at RemixEntry (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:2416:12)
at RemixBrowser (http://localhost:3000/build/_shared/chunk-A4C35FZK.js:3130:27)
printWarning @ react-dom.development.js:67
error @ react-dom.development.js:43
warnForPropDifference @ react-dom.development.js:8824
diffHydratedProperties @ react-dom.development.js:9645
hydrateInstance @ react-dom.development.js:10400
prepareToHydrateHostInstance @ react-dom.development.js:14616
completeWork @ react-dom.development.js:19458
completeUnitOfWork @ react-dom.development.js:22815
performUnitOfWork @ react-dom.development.js:22787
workLoopSync @ react-dom.development.js:22707
renderRootSync @ react-dom.development.js:22670
performSyncWorkOnRoot @ react-dom.development.js:22293
scheduleUpdateOnFiber @ react-dom.development.js:21881
updateContainer @ react-dom.development.js:25482
(anonymous) @ react-dom.development.js:26021
unbatchedUpdates @ react-dom.development.js:22431
legacyRenderSubtreeIntoContainer @ react-dom.development.js:26020
hydrate @ react-dom.development.js:26086
(anonymous) @ entry.client.tsx:4
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.
Unchecked runtime.lastError: The message port closed before a response was received.