Home > Software design >  How can I render Google Translate Widget only once using useEffect Hook in Next JS Project
How can I render Google Translate Widget only once using useEffect Hook in Next JS Project

Time:09-29

    import { useEffect } from "react";

    const GoogleTranslate = () => {
    useEffect(() => {
    let addScript = document.createElement("script");
    addScript.setAttribute(
      "src",
      "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"
    );

    document.body.appendChild(addScript);

    window.googleTranslateElementInit = googleTranslateElementInit;
    }, []);

    const googleTranslateElementInit = () => {
    return new window.google.translate.TranslateElement(
      {
        pageLanguage: "en",
        layout: google.translate.TranslateElement.InlineLayout.BUTTON,
      },
      "google_translate_element"
      );
     };
    return <div id="google_translate_element"></div>;
   };

   export default GoogleTranslate;

Here is my Component. I have used in a single page. But instead of once I am getting two separate instance of google translate button.

So, I want to know how to render only once this button.

Here is the UI image

Double Google translate button bug

CodePudding user response:

useEffect may run twice despite an empty dependency array in multiple circumstances (for example, if a parent component is re-rendering, or if you have React StrictMode enabled).

Hence, you are probably creating 2 elements inside useEffect.

You could analyze your code and check for potential unwanted re-renders or simply replace useEffect with this useEffectOnce hook:

import { useEffect, useRef, useState } from 'react';

export const useEffectOnce = (effect: () => void | (() => void)) => {
  const effectFn = useRef<() => void | (() => void)>(effect);
  const destroyFn = useRef<void | (() => void)>();
  const effectCalled = useRef(false);
  const rendered = useRef(false);
  const [, setVal] = useState<number>(0);

  if (effectCalled.current) {
    rendered.current = true;
  }

  useEffect(() => {
    if (!effectCalled.current) {
      destroyFn.current = effectFn.current();
      effectCalled.current = true;
    }

    setVal(val => val   1);

    return () => {
      if (!rendered.current) {
        return;
      }

      if (destroyFn.current) {
        destroyFn.current();
      }
    };
  }, []);
};

Just create a file with this code, and import and use it instead of useEffect.

  • Related