0

I created below two functions, to add and remove external src and <script> tags in my React app:

type Args = {
    src: string;
    id: string;
    onLoad?: () => void;
  };

  const addScript = ({ src, id, onLoad }: Args) => {
    const existing = document.getElementById(id);
    if (existing) {
      return existing;
    }
    const script = document.createElement('script');
    script.src = src;
    script.id = id;
    script.async = true;
    script.onload = () => {
      if (onLoad) {
        onLoad();
      }
    };
    document.body.appendChild(script);
    return script;
  };

  const removeScript = ({ id }: { id: string }) => {
    const script = document.getElementById(id);
    if (script) {
      document.body.removeChild(script);
    }
  };

I can then use it like:

useEffect(() => {
    const script = addScript({
      src: `https://domain/js/main.js`,
      id: 'ad-script',
      onLoad: () => {
        console.log('Script loaded!');
      },
    });
    return () => removeScript({ id: script.id });
  }, [id]);

Since I am using useEffect hook here. How do I create a custom React hook which returns above logic and I can use like: useScript('https://domain/js/main.js', 'my-script' );

meez
  • 3,783
  • 5
  • 37
  • 91

1 Answers1

0

The custom hook gets the same params needed for addScript. It stores the onLoad in a ref, so the useEffect won't trigger if the onLoad changes. This saves you the need to wrap onLoad with useCallback.

const useScript = ({ src, id, onLoad }: Args) => {
  const onLoadRef = useRef(onLoad);

  useEffect(() => {
    const script = addScript({ src, id, onLoad: onLoadRef.current?.() });
    
    return () => removeScript({ id: script.id });
  }, [src, id]);
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209