Как запустить эффект React.js при загрузке сторонней библиотеки с помощью useEffect?

Я пытаюсь создать <Drift> компонент, который монтирует сервис чата Drift:

Drift.js

import React, { useEffect, useState } from 'react'

export default function Drift (props) {
  const [drift, setDrift] = useState(undefined)

  // Evaluate the drift script once
  useEffect(() => {
    const script = document.getElementById('drift-script').innerHTML
    window.eval(script)
  }, [])

  // Poll for drift availability
  useEffect(() => {
    const poller = setInterval(() => {

      if (window.drift) {
        setDrift(window.drift)
        clearInterval(poller)

        console.log('setDrift(fn) called')
      }
    }, 500)

    return () => clearInterval(poller)
  }, [])

  // Configure the drift widget once
  useEffect(() => {
    console.log('drift depended effect triggered:')
    console.log(typeof drift)

    if (drift) {
      console.log('drift is defined')

      drift.config({
        messages: {
          welcomeMessage: 'Your code is being generated!'
        }
      })

      drift.on('ready', function (api) {
        console.log('drift ready')

        api.widget.hide()

        api.widget.show()
        api.showWelcomeMessage()
      })
    }
    else {
      console.log('drift is undefined')
    }
  }, [drift])

  return (
    <div>
      <script
        id="drift-script"
        dangerouslySetInnerHTML={{
          __html: `
          "use strict";

          !function() {
            var t = window.driftt = window.drift = window.driftt || [];
            if (!t.init) {
              if (t.invoked) return void (window.console && console.error && console.error("Drift snippet included twice."));
              t.invoked = !0, t.methods = [ "identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on" ], 
              t.factory = function(e) {
                return function() {
                  var n = Array.prototype.slice.call(arguments);
                  return n.unshift(e), t.push(n), t;
                };
              }, t.methods.forEach(function(e) {
                t[e] = t.factory(e);
              }), t.load = function(t) {
                var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script");
                o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js";
                var i = document.getElementsByTagName("script")[0];
                i.parentNode.insertBefore(o, i);
              };
            }
          }();
          drift.SNIPPET_VERSION = '0.3.1';

          drift.load('DRIFT_ID');
          `
        }}
      />

      { props.children }
    </div>
  )
}

Идея в том, что эффект запускается один раз window.drift становится определенным. Однако эффект запускается только один раз - когда window.drift не определен.

Полный журнал:

drift depended effect triggered:
Drift.js:30 undefined
Drift.js:51 drift is undefined
Drift.js:20 setDrift(fn) called

Как я могу создать ссылку на window.drift, которая приводит к выполнению эффекта после его определения?

2 ответа

Если бы можно было импортировать библиотеку, это было бы предпочтительнее:

import Drift from 'drift'

Если нет, я бы предложил запустить императивный код, не связанный с React, в index.js:

import ReactDOM from 'react-dom'
...
!function() {
  var t = window.driftt = window.drift = window.driftt || [];
  ...
ReactDOM.render(...)

Или выполнить скрипт из отдельного файла в index.htmlpublic папка при использовании create-реагировать-приложение):

<body>
  <div id='root'>
  <script src="drift.js"></script>
</body>

Вы можете добавить клиент Drift в качестве скрипта в заголовок тега.

Рабочий пример

Редактировать kind-shape-1wus8


Чтобы это работало, добавьте содержимое скрипта, заданного параметром drift, в виде текста в файле. Затем загрузите это как скрипт в useEffect путем создания тега сценария.

useEffect(() => {
  const script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.innerHTML = drift;
  document.head.appendChild(script);
  return () => script.parentNode.removeChild(script);
}, []);

Кроме того, если вы хотите, чтобы это было доступно в вашем приложении, вы можете просто добавить его в качестве встроенного тега сценария в HTML-файл индекса шаблона, который используется веб-пакетом.

Другие вопросы по тегам