我有一个相对简单的问题,试图将内联脚本添加到React组件。到目前为止我有:

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>
                    
                    <script src="https://use.typekit.net/foobar.js"></script>
                    <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}></script>
                </article>
            </DocumentTitle>
        );
    }
};

我也试过:

<script src="https://use.typekit.net/foobar.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>

这两种方法似乎都不能执行所需的脚本。我猜我错过了一件简单的事。有人能帮忙吗?

PS:忽略foobar,我有一个真正的id实际上在使用,我不想分享。


当前回答

要获得支持加载状态和错误处理的更完整的useScript实现,请查看useHooks。

使用

function App() {
  const status = useScript(
    "https://pm28k14qlj.codesandbox.io/test-external-script.js"
  );
  return (
    <div>
      <div>
        Script status: <b>{status}</b>
      </div>
      {status === "ready" && (
        <div>
          Script function call response: <b>{TEST_SCRIPT.start()}</b>
        </div>
      )}
    </div>
  );
}

Hook

function useScript(src) {
  // Keep track of script status ("idle", "loading", "ready", "error")
  const [status, setStatus] = useState(src ? "loading" : "idle");
  useEffect(
    () => {
      // Allow falsy src value if waiting on other data needed for
      // constructing the script URL passed to this hook.
      if (!src) {
        setStatus("idle");
        return;
      }
      // Fetch existing script element by src
      // It may have been added by another intance of this hook
      let script = document.querySelector(`script[src="${src}"]`);
      if (!script) {
        // Create script
        script = document.createElement("script");
        script.src = src;
        script.async = true;
        script.setAttribute("data-status", "loading");
        // Add script to document body
        document.body.appendChild(script);
        // Store status in attribute on script
        // This can be read by other instances of this hook
        const setAttributeFromEvent = (event) => {
          script.setAttribute(
            "data-status",
            event.type === "load" ? "ready" : "error"
          );
        };
        script.addEventListener("load", setAttributeFromEvent);
        script.addEventListener("error", setAttributeFromEvent);
      } else {
        // Grab existing script status from attribute and set to state.
        setStatus(script.getAttribute("data-status"));
      }
      // Script event handler to update status in state
      // Note: Even if the script already exists we still need to add
      // event handlers to update the state for *this* hook instance.
      const setStateFromEvent = (event) => {
        setStatus(event.type === "load" ? "ready" : "error");
      };
      // Add event listeners
      script.addEventListener("load", setStateFromEvent);
      script.addEventListener("error", setStateFromEvent);
      // Remove event listeners on cleanup
      return () => {
        if (script) {
          script.removeEventListener("load", setStateFromEvent);
          script.removeEventListener("error", setStateFromEvent);
        }
      };
    },
    [src] // Only re-run effect if script src changes
  );
  return status;
}

其他回答

Alex Mcmillan提供的答案对我帮助最大,但对于更复杂的脚本标记不太适用。

我稍微调整了他的答案,提出了一个解决方案的长标签与各种功能,另外已经设置“src”。

(对于我的用例,脚本需要生活在头部,这是反映在这里):

  componentWillMount () {
      const script = document.createElement("script");

      const scriptText = document.createTextNode("complex script with functions i.e. everything that would go inside the script tags");

      script.appendChild(scriptText);
      document.head.appendChild(script);
  }

以下是我如何最终能够在React JS代码中添加两个外部JavaScript文件:

以下是我遵循的步骤。

步骤1: 我在react-app文件夹路径中使用npm I React-Helmet从终端安装了React-Helmet。

步骤2: 然后我添加了import {Helmet} from "react-helmet";头在我的代码。

步骤3: 最后,在我的代码中这是 我如何使用Helment添加外部JS文件

<Helmet>
    <script src = "path/to/my/js/file1.js" type = "text/javascript" />
    <script src = "path/to/my/js/file2.js" type = "text/javascript" />  
</Helmet>

你可以试着使用下面的语句:

确保你相信剧本

 <script>{`
            function myFunction(index, row) {
                return index;
            }
        `}
</script>

解决方案视场景而定。就像在我的情况下,我必须加载一个日历嵌入在一个react组件。

Calendly查找一个div并从它的data-url属性读取,并在该div中加载iframe。

第一次加载页面时一切正常:首先,呈现带有data-url的div。然后日历脚本添加到主体。浏览器下载并评估它,我们都高兴地回家了。

问题来了,当你导航离开,然后回到页面。这一次脚本仍然在主体中,浏览器不会重新下载和重新评估它。

Fix:

在componentWillUnmount上找到并删除脚本元素。然后重新安装,重复上述步骤。 进入.getScript美元。它是一个漂亮的jquery助手,接受一个脚本URI和一个成功回调。一旦加载了脚本,它就计算它并触发你的success回调。我所要做的是在我的componentDidMount $. getscript (url)。我的渲染方法已经有日历div。它工作顺利。

你必须为这个脚本创建一个组件,你调用这个组件作为一个标准的ES6脚本标记

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>
                </article>
            </DocumentTitle>
        );

        class Component extend Index.App {  
             <script src="https://use.typekit.net/foobar.js" />
             <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}/>
        }
    }
};