見出し画像

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.


いいなと思ったら応援しよう!