ゆとり日記

心にゆとりを持って生きたいプログラマーの雑記です。気が向いたら書きます。

React Hooksを使ってJavaScriptを動的に取得する

「Next.js、全然分からない」「Reactやるのが久しぶりで何もわからない」

このようにボヤきながらReactと向き合う中で、しばらくハマっていたことが解決したのでブログに纏めておきます。

何が解決したのか

独立したJavaScriptファイルを取得Custom Hookを作ることができました。

自前のソースコードを書くだけなら要らぬ心配ですが、サードパーティスクリプトGoogle Analyticsなど)をSPAでそのまま使いたい場合があると思います。そういう時に使えるかもしれない裏技です。

動作サンプル

https://codepen.io/rukiadia/pen/JjYadOL

画面表示後にjQueryのコードをCDNから取得しています。あくまでサンプルなので、jQuery云々は気にしないでください。

  • react v16.13.1
  • react-dom v16.13.1

Reactのバージョンはこちらで動作させています。

コードの説明

const { useEffect, useState } = React;
// 手元で試す場合は以下
// import React, { useEffect, useState } from "react";

const useScript = (src) => {
   // 「取得の完了」「取得時のエラー判定」をLocal Stateとして扱う
   const [state, setState] = useState({
     loaded: false,
     hasError: false
   });

  useEffect(() => {
    const scriptElement = document.createElement('script');
    scriptElement.src = src;

    const onLoadFunction = () => {
      setState({
        loaded: true,
        hasError: false
      });
    };

    const onErrorFunction = () => {
      // 取得失敗時、コンポーネント側でエラーハンドリングするフラグを立てる
      setState({
        loaded: true,
        hasError: true
      });
    };

    scriptElement.addEventListener('load', onLoadFunction);
    scriptElement.addEventListener('error', onErrorFunction);
    const wrapper = document.getElementById('wrapper');
    wrapper.appendChild(scriptElement);

    return () => {
      // load、error時の処理を関数化しておけば、ここでremoveEventListenerできる
      scriptElement.removeEventListener('load', onLoadFunction);
      scriptElement.removeEventListener('error', onErrorFunction);
    }
  }, []);

  return [state.loaded, state.hasError];
};
  • 動的にscript要素を生成してJavaScriptを取得する。
  • 「取得の完了」か「取得の成否」をLocal Stateとして管理し、結果をuseScriptを使っているコンポーネント側に返す。

やっていることを箇条書きにすると、このようになります。

実装時に気をつけること

動的に生成したscriptで取得したJavaScriptからはdocument.writeを呼び出せないので、document.writeが含まれている場合はこの手法が使えません。非同期取得を試す前に取得相手の中身は軽く見ておくようにしましょう。

Custom Hookの命名はuseで始める

ja.reactjs.org

カスタムフックとは、名前が ”use” で始まり、ほかのフックを呼び出せる JavaScript の関数のことです。

自作する場合は自由な命名をしないように意識しましょう。

ja.reactjs.org

"use"で始めなくても動かせてしまうので、公式で提供されているESLintを使っておくのが安全です。

参考資料